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形式传递的文件的各种信息.
Syntax | Expansion Result | Example |
---|---|---|
%~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 |
%~pn1 | p和 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\ |
%%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—
相关文章推荐
- HDU-4635-Strongly connected(强连通分量)
- 2014年第五届蓝桥杯C/C++程序设计本科B组决赛 Log大侠(编程大题)
- static全局变量与普通的全局变量的区别/static局部变量和普通局部变量的区别/static函数与普通函数的区别
- <<数学>>圆角矩形,矩形重叠(一)
- cocos2d-x lua 位操作
- Flask config.py 项目加载配置文件
- Linux中的文件描述符与打开文件之间的关系
- java中的线程-继承thread-实现runnable接口以及线程同步
- Selenium - Best practices
- 如何将一张披萨饼平均分成11份
- android-async-http框架库使用基础
- Leetcode 171. Excel Sheet Column Number
- dialog 中装listview并让每一个item分隔悬空,并具有radiobutton的效果
- 搭建wyquery环境
- android-SQLiteOpenHelper又称本地库
- 点击最小化按钮后,找不到AOPR软件了怎么办?
- Storyboard学习二(UIButton)
- CASE 工具有哪些
- 283. Move Zeroes
- android开源框架android-async-http使用