您的位置:首页 > 其它

WindowsBatchScripting_B

2016-05-13 14:48 459 查看

Command-line arguments

command-line arguments即 command-line parameters(命令行参数)在batch脚本中可以通过
%1, %2,....,%9
来获取. 可以有多于9个的参数 – 参见 how to loop over all of them.

%0
语法不指向命令行参数, 而是执行batch文件自身.

e.g. 测试是否提供了第一个命令行参数

if not -%1-==-- echo Argument one provided
if -%1-==-- echo Argument one not provided & exit /b


使用
SHIFT
(for each command-line argument, …)来更强健地循环遍历命令行参数:

:argactionstart
if -%1-==-- goto argactionend
echo %1 & REM Or do any other thing with the argument
shift
goto argactionstart
:argactionend


使用
SHIFT
循环遍历命令行参数, 但不修改 %1, %2…

call :argactionstart %*
echo Arg one: %1 & REM %1, %2, etc. are unmodified in this location
exit /b

:argactionstart if -%1-==-- goto argactionend echo %1 & REM Or do any other thing with the argument shift goto argactionstart :argactionend
exit /b


将命令行参数传递给环境变量:

setlocal EnableDelayedExpansion
REM Prevent affecting possible callers of the batch
REM Without delayed expansion, !arg%argno%! used below won't work.
set argcount=0
:argactionstart
if -%1-==-- goto argactionend
set /a argcount+=1
set arg%argcount%=%1
shift
goto argactionstart
:argactionend

set argno=0
:loopstart
set /a argno+=1
if %argno% gtr %argcount% goto loopend
echo !arg%argno%! & REM Or do any other thing with the argument
goto loopstart
:loopend


遍历所有命令行参数, 虽然不是个强健(robust)的方案:

for %%i in (%*) do (
echo %%i
)


代码很优雅但不够强健, 没有考虑包含通配符wildcards(
*,?
)的参数. 特别是上面的命令会用符合的文件名替换包含通配符(
*,?
)的参数, 或者没有文件符合的时候丢弃它们.

不管怎样, 上面的循环在参数不包含通配符的时候可以很好地工作.

e.g. 非强健方式, 找到命令行参数个数

set argcount=0
for %%i in (%*) do set /a argcount+=1


再一次, 对于包含通配符的参数不成立.

按Windows Vista机器的测试经验, 参数个数最大可以达到4000. 在XP和Win7上个数可能不同.

传递参数到batch脚本时, 参数使用的分隔字符可以是:

- space(空格)

- comma(逗号)

- semicolon(分号)

- equal sign(等于号)

- tab character(制表符)

下面的命令行中的参数都是一样的:

-
test.bat a b c d


-
test.bat a,b,c,d


-
test.bat a, b, c, d


-
test.bat a;b;c;d


-
test.bat a=b=c=d


-
test.bat a b,c;,;=d


即使是
a b,c;,;=d
也可以, 一系列的分隔符可以被当做单个分隔符.

要将 space, comma, semicolon放入参数值中, 可以将值包围在引号(quotation marks)中然后传入. 不过引号也变为参数值的一部分.

当引用脚本中的参数时, 要将外面的引号剔除, 可以使用
%~<number>
– 参见Percent tilde

Parameters / Arguments at ss64

Escape Characters, Delimiters and Quotes at ss64

Using batch parameters at Microsoft

Wildcards

许多命令接收文件名通配符–即并不代表自身意义的字符, 而是表示开启了文件名组的匹配模式.

Wildcards(通配符):

- *(asterisk)-星号: 任何字符序列

- ? (question mark)-问号: 单个字符, 除了(“.”), 或者是在一个maximum period-free(无句号)文件名的末尾的一系列问号的一部分, 可能是 zero number of characters(零个字符), 参见澄清的例子.

ex.

-
dir *.txt


匹配 Myfile.txt, Plan.txt以及其他有 .txt后缀的文件

-
dir *txt


无需加入句号(period). 这可以匹配没有遵循句号约定的文件名, 如 myfiletxt.

-
ren *.cxx *.cpp


将所有以 .cxx作为后缀名的文件, 重命名为 .cpp后缀的文件.

-
dir a?b.txt


- 匹配 aab.txt, abb.txt. a0b.txt, etc.

- 不匹配 ab.txt, 问号后面跟的字符不是单个问号或句号, 和zero character(零字符)不匹配.

- 不匹配 a.b.txt, 问号无法匹配一个句号.

-
dir ???.txt


匹配 .txt, a.txt, aa.txt, aaa.txt. 序列中的问号后面跟句号, 可以和 zero number of characters 匹配.

-
dir a???.b???.txt???


匹配 a.b.txt. 当最后一个问号后面没有跟句号, 它仍然是在文件名的maximum period-free部分的一个序列.

-
dir ????????.txt & @REM eight question marks


和 *.txt匹配的类似, 因为每个文件都有在 .txt前不超过8字符的短名称(short file name).

短名称的离奇之处:

通配符匹配同时会作用在长文件名, 以及隐藏的 short 8 chars+period+3 chars(8字符名.3字符后缀) 文件名上. 这样的意外情况会引起问题.

不像其他操作系统的 shell, cmd.exe自身不会应用 wildcard expansion(通配符扩展) (将包含通配符的pattern用匹配pattern的文件名替换). 这其实是每个程序处理通配符时的责任.

这可以让
ren *.txt *.bat
这样的命令工作, 因为 ren命令实际上看到的是
*
通配符, 而不是一列匹配通配符的文件. 于是
echo *.txt
就不会显示当前文件夹下符合pattern的文件, 而是按字面意思显示
*.txt
.

另一个结果: 可以写
findstr a.*txt
, 无需担心
a.*txt
被一些当前文件夹下的文件名替换掉. 还有, 可以递归
findstr /s pattern *.txt
, 在一些其他操作系统中,
*txt
部分可能会被所找到的文件名所替换, 从而就无视了嵌套的文件夹.

接受通配符的命令包括: ATTRIB, COPY, DIR, FINDSTR, FOR, REN, etc.

Wildcards at ss64

Using wildcard characters at Microsoft

User input

可以使用下列方法来获得用户输入:

- SET /P 命令

- CHOICE ml

- 使用
type con >myfile.txt
, 对于多行的用户输入, 使用 Ctrl+Z来结束.

Percent tilde

当命令行参数(command-line argument )包含文件名, 可以用特殊语法来获取文件的各种信息.

下面的语法扩展了以%1形式传递的文件的各种信息.

SyntaxExpansion ResultExample
%~1%1去除包围的引号n/a
%~f1带盘符的全路径C:\Windows\System32\notepad.exe
%~d1盘符C:
%~p1末尾带反斜杠的无盘符路径\Windows\System32\
%~n1对于文件, 是不带路径和扩展名的文件名.
对于目录则是目录名
notepad
%~x1包括句号的文件扩展名.exe
%~s1修改 f, n, x 以使用短名称n/a
%~a1文件属性(attribute)–a——
%~t1文件上次被修改的日期和时间02.11.2006 11:45
%~z1文件大小151040
%~pn1p和 n的组合\Windows\System32\notepad
%~dpnx1多个字母的组合C:\Windows\System32\notepad.exe
%~$PATH:1当前PATH变量下的文件夹中找到的第一个匹配的路径,
没有匹配的话返回空string
n/a
%~n0将 %~n应用到 %0; 不带扩展名的batch名称tildetest
%~nx0将 %~nx应用到 %0; batch的名称tildetest.bat
%~d0将 %~f应用到 %0; batch盘符C:
%~dp0将 %~dp应用到 %0; batch的文件夹, 后面跟反斜杠C:\Users\Joe Hoe\
FOR命令创建的相同语法可以作用于single-letter(单字母)变量, 如
%%i
. 更多信息可以参见
call /?
for /?
.

Parameters / Arguments at ss64

Using batch parameters at Microsoft

for at Microsoft

Functions

function(函数)即 subprogram(子程序)可以通过CALL, labels, SETLOCAL, ENDLOCAL来实现.

e.g. 检测 arithmetic power(算术平方)

@echo off
call :power 2 4
echo %result%
rem Prints 16, determined as 2 * 2 * 2 * 2
goto :eof

rem __Function power______________________
rem Arguments: %1 and %2
:power
setlocal
set counter=%2
set interim_product=%1
:power_loop
if %counter% gtr 1 (
set /a interim_product=interim_product * %1
set /a counter=counter - 1
goto :power_loop
)
endlocal & set result=%interim_product%
goto :eof


在function最后的
goto :eof
不是必要的, 通常存在超过一个function的时候他才是必须的.

result变量可以在command line上保存以及指定:

@echo off
call :sayhello result=world
echo %result%
exit /b

:sayhello
set %1=Hello %2
REM Set %1 to set the returning value
exit /b


上例中,
exit /b
用来代替
goto :eof
, 效果一样.

Note: equal sign(等号) 是用来分隔参数的. 下面各项都一样:

call :sayhello result=world


call :sayhello result world


call :sayhello result,world


call :sayhello result;world


参见 Command-line arguments

Calculation

Batch脚本可以做简单的32-bit integer arithmetic(整数运算) 以及 bitwise manipulation(比特位操作): 使用
SET /a
命令. 支持的最大整数:
2147483647 = 2 ^ 31 - 1
, 最小整数
-2147483648 = - (2 ^ 31)
, 通过赋值的trick来达成:
set /a num=-2147483647-1
. 语法和老式C语言一样.

算术操作包括
*, /, % (modulo), +, -
. batch中的modulo(取模)必须输入
%%
.

Bitwise操作将数字解译为 32 binary digits(32位二进制数). 它们是
~
(complement补),
&
(and与),
|
(or或),
^
(xor异或),
<<
(left shift左移),
>>
(right shift右移).

negation (取反)的逻辑操作是
!
: 0变成1, 非0变成0;

combination(组合操作)
,
: 允许一个set命令中有多个计算.

组合赋值操作的模式为:
+=
, 即
a+=b
意思是
a=a+b
;
a-=b
意思是
a=a-b
; 类似的:
*=, /=, %=, &=, ^=, |=, <<=, and >>=
.

precedence order(有限次序)

1. ( )

2. * / % + -

3. << >>

4. &

5. ^

6. |

7. = *= /= %= += -= &= ^= |= <<= >>=

8. ,

字面量可以按如下输入: 十进制: decimal (1234), 十六进制 hexadecimal (0xffff, leading 0x), 八进制 octal (0777, leading 0).

对于负数, 内部的bit表示是 two’s complement. 在算术运算和bit操作之间提供了一种联系; e.g. -2147483648 用 0x80000000表示,
set /a num=~(-2147483647-1)
产生 2147483647, 等于 0x7FFFFFFF(
set /a num=0x7FFFFFFF
)

对于命令解释器来说一些操作有特殊含义, 表达式需要用引号包围它们来使用:

set /a num="255^127"


set /a "num=255^127"


两种引号放置方式都可以

set /a num=255^^127


使用 ^来转义 ^, 代替引号.

ex.

-
set n1=40 & set n2=25


set /a n3=%n1%+%n2%


使用标准百分号来进行变量扩展.

-
set n1=40 & set n2=25


set /a n3=n1+n2


有了 /a选项, 就无需在变量名上用百分号包围

-
set /a num="255^127"


将 “^”放在引号里面可以防止命令解释器解析到它的特殊含义.

-
set /a n1 = (10 + 5)/5


有 /a选项情况下, 等号 =前后的space不起作用.

However, getting used to it lends itself to writing “set var = value” without /a, which sets the value of “var ” rather than “var”.

-
set /a n1=2+3,n2=4*7


两次计算

-
set /a n1=n2=2


和 n1=2,n2=2有一样效果

-
set n1=40 & set n2=25 & set /a n3=n1+n2


和预期的一样.

-
set /a n1=2,n2=3,n3=n1+n2


和预期的一样.

-
set n1=40 & set n2=25 & set /a n3=%n1%+%n2%


除非n1和 n2预先设置好, 否则无法工作. “%n1%” 和 “%n2%”的变量规格会在第一个 set命令被执行前进行扩展. 去掉百分号则可以工作.

-
set /a n1=2,n2=3,n3=%n1%+%n2%


除非n1和 n2预先设置好, 否则无法工作, 和上例的理由一样.

-
set /a n1=0xffff


用16进制记号设置 n1.

-
set /a n1=0777


用8进制记号设置n1.

e.g. 打印prime numbers(质数):

使用从2到n的开根的数作为除数 http://coolshell.cn/articles/3738.html

@echo off
setlocal
set n=1
:print_primes_loop
set /a n=n+1
set cand_divisor=1
:print_primes_loop2
set /a cand_divisor=cand_divisor+1
set /a cand_divisor_squared=cand_divisor*cand_divisor
if %cand_divisor_squared% gtr %n% echo Prime %n% & goto :print_primes_loop
set /a modulo=n%%cand_divisor
if %modulo% equ 0 goto :print_primes_loop & REM Not a prime
goto :print_primes_loop2


set at ss64.com

set at Microsoft

Finding files

使用 DIR, FOR, FINDSTR, FORFILES, WHERE 来寻找文件.

ex.

-
dir /b /s *base*.doc*


输出当前文件夹下以及子文件夹下的文件, 扩展名前的文件名包含”base”, 扩展名以”doc”开始, 包括”doc”和”docx”. 文件输出全路径, 一行一个文件.

-
dir /b /s *.txt | findstr /i pers.*doc


文件包括全路径以及 findstr过滤命令所支持的有限的regular expression(正则表达式)的组合结果, 产生了一个多功能又强大的组合, 可以通过文件名和目录名来寻找文件;

-
for /r %i in (*) do @if %~zi geq 1000000 echo %~zi %i


当前文件夹以及子文件夹下, 文件size大于或等于1,000,000 bytes的, 以byte为单位输出文件size, 以及文件全路径. 对于
%~zi
语法, 参见 Percent tilde.

-
forfiles /s /d 06/10/2015 /c "cmd /c echo @fdate @path"


当前文件夹以及子文件夹下, 在2015/6/10及之后修改过的文件, 打印文件修改日期和全路径. /d后面的日期格式以locale为准. 这样, 即可寻找最近修改的文件.

-
(for /r %i in (*) do @echo %~ti :: %i) | findstr 2015.*::


在当前文件夹下递归查找, 输出最后修改日期在2015年内的文件. 将修改日期和事件放在double colon(双冒号::)前面. 只要Windows和locale版本是包含four-digit年份的格式, 就可以工作.

双冒号是为了确保 findstr命令和日期及时间匹配, 而不是文件名.

-
for /r %i in (*) do @echo %~ti | findstr 2015 >NUL && echo %i


同上, 在2015年内修改的. 不同的是, 只输出文件, ,没有修改日期.

-
findstr /i /s /m cat.*mat *.txt


通过内容查找文件. 通过 正则表达式
cat.*mat
实施全文本查找, 条件是所有以 .txt结尾的文件, 输出文件名. /m开关确保只有文件名被输出.

-
where *.bat


输出在当前目录以及 PATH目录下所有的 .bat文件.

Keyboard shortcuts

使用Win+R, 输入cmd.exe, 即可在标准控制台(console)中使用Windows命令行, 这里有许多键盘快捷键, 包括功能键:

Tab: 当前文件夹下的文件名, 文件夹名补全. 可补全的部分通常是最后的 space-free(无空格)部分, 但如果用了引号就不一样了. 通常的命令都能补全文件和文件夹, 但 cd命令只对文件夹起作用.

Up Down arrow: 从command历史中搜索命令, 每次一个.

Escape: 擦除当前输入的命令.

F1: 一个个字符地打出commad历史中上次输入的命令.

F2: 请求输入一个字符, 会出现command 历史中的上个命令的最短的前缀, 其中不包含输入的那一个字符. e.g. 前一个命令: echo Hello world, 如果输入 o, 就出现 ech.

F3: 进入command历史中的前一个命令. 重复F3不起作用.

F4: 请求输入一个字符, 以当前光标位置开始, 删除一直到所输入的字符(不包括)中间的部分.e.g. 如果输入 echo Hello world, 光标在 H前, F4输入 w, 结果就是 echo world.

F5: 进入command历史中的前一个命令.

F6: Control+Z 字符.

F7: 弹出一个command历史的字符列表, 可以用Up,Down选择命令, 回车直接执行.

F8: 给出一个字符串, 会在command历史中搜寻以此字符串为前缀的命令以显示, 每次一个.

F9: 输入数字, 按序号执行command历史中的命令.

Alt + F7: 清除command历史.

上述也被认为是command prompt keyboard shortcuts.

上述快捷键的可用性看起来与运行中的 DOSKEY无关.

Windows Keyboard shortcuts at ss64.com

doskey at Microsoft

Perl one-liners

有些任务可以用Perl one-liners方便地完成. Perl脚本语言源自另一个操作系统. 由于许多Windows环境安装了Perl, Perl one-liners对Windows batch脚本来说是自然兼容的后缀.

ex.

-
echo "abcbbc"| perl -pe "s/a.*?c/ac/"


让Perl像sed(流编辑器)一样, 功能是使用正则表达式, 以支持文本替换.

-
echo a b| perl -lane "print $F[1]"


Perl当做cut命令, 显示列中第二个区域内容, 本例为 b. 使用
$F[2]
显示第三块区域; 索引从0开始. Native方案是
FOR /f
.

-
perl -ne "print if /\x22hello\x22/" file.txt


Perl当做grep 或 FINDSTR, 输出file.txt中符合if后面正则表达式的各行. Perl regular expressions比FINDSTR强大许多.

-
perl -ne "$. <= 10 and print" MyFile.txt


Perl当做 head -10 command, 输出文件中的前10行.

-
perl -e "sleep 5"


等待5秒.

-
for /f %i in ('perl -MPOSIX -le "print strftime '%Y-%m-%d', localtime"') do @set isodate=%i


将ISO格式的当前时间放入 isodate变量.

-
perl -MWin32::Clipboard -e "print Win32::Clipboard->Get()"


将文本内容输出到剪贴板. 把命令存储到 getclip.bat中, 就有了一个方便的 getclip命令来实现 CLIP.

在网上, Perl one-liners经常在另一个操作系统中的command-line convention中贴出, 用单引号apostrophe (‘) 代替 Windows的quotation marks双引号. 这些都需要在Windows中调整.

Perl One-Liners by Peteris Krumins at github.com

Why doesn’t my Perl one-liner work on Windows? at stackoverflow.com

W:One-liner program#Perl

Limitations

没有和Linux类似的 touch命令. 这里的 touch会修改文件的 last-modification timestamp (最后修改的时间戳), 而不会改变其内容.

workaround, 可读性和可适应性较模糊:

-
copy /b file.txt+,,


Windows recursive touch command at superuser.com

Windows version of the Unix touch command at stackoverflow.com

—TBC—

—YCR—
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: