https://en.wikibooks.org/wiki/Windows_Batch_Scripting
本文针对32-bit Windows命令, 应用到现代Windows版本, 基于WIndows NT环境. 它不是针对特定DOS环境和基于DOS的操作系统, 比如Win95, Win98, WinME, 它们的 Microsoft-supplied命令解释器起始是 DOS程序, 而不是 Win32程序.
使用 VER
命令可以查看 cmd.exe的版本.
本文描述如何使用 Windows NT 命令解释器, 如果接收, 解析, 处理用户命令. 然后描述各种命令.
要扩展浏览 Windows命令和帮助, 使用 help
命令. 要对特定命令帮助信息浏览, 使用 /?
参数.
本文主题是 “batch programming”, batch仅仅是指 MS DOS的batch文件以及 Windows命令解释器.
其他的主题包含”batch file programming”, “batch file scripting”, “Windows batch command”, “Windows batch file”, “Windows command line”, “Windows command prompt”, “Windows shell scripting”.
将command line(命令行)解析为一系列的命令时复杂的, 命令解释器之间都有一些varies subtly(微妙变化). 不过, 它们主要由四个方面:
- Variable substitution
在command line上通过variable specifications(变量规格)进行检索, 如果有的话就用这些变量内容代替找到的文本.
- Quoting
特殊字符可以quoted(用引号括起), 移除它们的特殊含义.
- Syntax
command line是根据一种语法发展出的一系列命令.
- Redirection
Redirection specifications(重定向规格)应用, 在序列中的单个命令被执行前, 从command line中移除.
command line可以包含variable specifications. 其中有一个 %
字符, 后面跟一个名字. 名字由第二个 %
符结束, 除了特例, 如batch文件参数 %1, %2
…
variable specifications会被值代替.
- 对于和环境变量名一致的variable specifications, 将被命名的环境变量值替代. e.g. %PATH%
被 PATH环境变量的值代替.
- 对于命名了batch file参数的variable specifications(比如non-negative(非负)小数数字), 会在batch文件被运行的时候被参数的值所替代. (参见 SHIFT
).
e.g. %2
被第二个bacth文件参数的值替代.
使用 SET
命令时一些变量名是不可见的. 可以使用 %记号来读取 – help set
.
特殊变量名以及其扩展含义:
Name | Replacement Value Used |
---|---|
%CD% | 当前目录, 如果不是当前驱动器的根目录, 不会以斜杠结尾 |
%TIME% | HH:MM:SS.mm格式的系统时间 |
%DATE% | 根据localization(本地化)而定的系统日期 |
%RANDOM% | 产生一个0~32767之间的伪随机(pseudo-random)数 |
%ERRORLEVEL% | error level, 从最后执行的命令中, 或者从最后执行的batch脚本中返回 |
%CMDEXTVERSION% | 当前 cmd.exe所用的Command Processor Extensions的版本号 |
%CMDCMDLINE% | 当前 cmd.exe使用的command line的内容 |
Windows Environment Variables at ss64.com
Command shell overview at Microsoft
除了百分号之%
外, 可以利用控制命令语法, 解除它们的特殊含义.
- 在包含特殊字符串的string(字符串)外面用引号包围起来
- 可以在特殊字符前使用一个脱字符caret (^
), 一个转义字符. 在一个pipe(管道)后的命令中, 需要使用三个caret(^^^
).
https://en.wikipedia.org/wiki/Caret_notation
需要引号和转义的特殊字符通常是: <, >, |, &, ^
在某些情况下, !
和 \
可能需要转义; 一个 newline(换行符)也可以使用caret转义.
当使用引号包围string时, 它们就作为传递给命令的参数的一部分. 对应地, 当使用 caret作为转义字符, caret不会变成参数的一部分.
百分号%
是个特例. 在command line上, 它无需引号或转义, 除非它们两个是为了标识一个变量, 如 %OS%
. 但是在batch文件中, ix使用双百分号(%%
)来产生(yield)单个的百分号(%
).
给百分号加上引号或者前置caret是无效的.
ex.
- echo "Johnson & son"
显示整个string, 而不是在 &字符处分离. 引号也显示.
- echo Johnson ^& son
和上面类似, 但是在特殊字符ampersand(&)前使用 caret. 没有引号显示.
- echo Johnson & son
不使用转义符, “son”被解释为一个单独的命令, 通常会引出 error message: command son is not found.
- echo A ^^ B
显示 A ^ B. caret也需要转义, 否则会被解释为: 转义一个space.
- if 1 equ 1 ^
echo Equal &^
echo Indeed, equal
显示2个string. 行尾的caret转义了 newline, 因此三行现在被当做了一行. 在第一个caret之前的space是必须的, 否则 1会和下面连接起来产生(yield)一个 1echo.
- attrib File^ 1.txt
因为对space的转义不工作, 因此不会显示”File 1.txt”的attribute. 如果使用引号, “File 1.txt”则可以工作.
- echo The ratio was 47%
如果从batch文件中运行, 百分号会被忽略.
- echo The ratio was 47%%
如果从batch文件中运行, 百分号会被输出一次.
- set /a modulo=14%%3
如果从batch文件中运行, 将modulo变量设为2 – 14除以3的余数. 单个 %
无法工作.
- for %%i in (1,2,3) do echo %%i
如果从batch文件中运行, 输出是 1, 2, 3
- echo %temp%
即使是从batch文件中运行, 也是输出temp变量的内容. 在batch中使用百分号获取环境变量, 以及传递参数无需转义.
- echo //comment line | findstr \//
FINDSTR
命令使用反斜杠(backslash )来转义. 不像 caret, 这是在命令的内部而且command shell是不知道的.
Syntax : Escape Characters, Delimiters and Quotes at ss64
Command shell overview at Microsoft
set at Microsoft
command line是根据语法发展为一系列的命令. 在这个语法中, simple command(简单命令)可以是由pipline(管道)中结合而来的, 也能转而结合为compound command(复合命令), 最终可以变为parenthesized commands(括起的命令)
simple command(简单命令)就是个命令名称, 一个命令结尾, 以及一些 redirection specification(重定向规格). e.g. dir *.txt > somefile
pipeline是几个简单命令的组合, 使用”pipe”metacharacter(元字符) “|” (vertical bar). 通过pipe, 在简单命令的标准输出前置vertical bar, 和简单命令的标志输入连接起来. 命令解释器并行地运行在pipeline上所有的简单命令.
e.g. dir *.txt | more
复合命令是一组由conjunction(联合)分隔开的pipeline. pipeline逐个地被执行, 一个接一个, 而且conjunction可控制命令解释器是否去执行下一个pipeline.
e.g. (包含两个pipeline, 各自都是个简单命令) move file.txt file.bak && dir > file.txt
.
- &
- unconditional conjunction(无条件联合) 当前pipeline执行完毕后, 下个pipeline总是执行.
- &&
- positive conditional conjunction - 如果当前pipeline执行完毕并且 zero 退出状态, 下一个会执行.
- ||
- negative conditional conjunction - 如果当前pipeline执行完毕并且 non-zero 退出状态, 下一个会执行.
parenthesized command是个括号中的复合命令(e.g. ( and )
). 从语法观点看, 这将复合命令变为一个简单命令, 其全部的输出可被重定向.
e.g. 命令行 ( pushd temp & dir & popd ) > somefile
将整个复合命令的输出 ( pushd temp & dir & popd )
重定向到 somefile
.
Conditional Execution at ss64.com
Using parenthesis/brackets to group expressions at ss64.com
Command shell overview at Microsoft
Redirection specification(重定向规格)应用时, 它会在序列中的单个命令被执行前, 从command line中被移除. 重定向规格控制标准输入, 标准输出, 标准错误文件句柄在哪里处理命令.
它们会覆盖文件句柄中任何从pipeline返回的结果.
重定向标记(signs) >
和 >>
前缀1表示标准输出(和没有前缀一样), 前缀2是标准错误.
重定向规格:
- < filename
重定向标准输入以读取文件内容
- > filename
重定向标准输出以写入文件, 覆盖之前的文件内容
- >> filename
重定向标准输出以写入文件, 附加到之前文件内容的尾部.
- >&h
重定向到句柄 h, 0代表标准输入, 1是标准输出, 2是标准错误, 以及其他.
- <&h
从句柄 h重定向.
ex.
- dir *.txt >listing.log
将 dir 的output重定向到 listing.log
- dir *.txt > listing.log
同上, 文件名前面的空格不起作用. 不过, 如果是在command window输入的命令, 在输入”> l”后用tab键 auto-completion(自动补全)是可以的, 不过对于”>listing.log”则不行.
- dir *.txt 2>NUL
将dir的error重定向到NUL(空).
- dir *.txt >>listing.log
将 dir 的output重定向到 listing.log, 附加到文件末尾. 这样在执行重定向命令前的文件内容不会丢失.
- dir *.txt >listing.log 2>&1
将 dir 的output和error信息一起重定向到 listing.log.
- dir *.txt >listing.log 2>listing-errors.log
将 dir 的output重定向到 listing.log, error重定向到listing-errors.log
- >myfile.txt echo Hello
重定向可以前置
- echo Hello & echo World >myfile.txt
只有第二个echo被重定向
- (echo Hello & echo World) >myfile.txt
两个echo都被重定向
- type con >myfile.txt
将console input (con)重定向到文件. 于是, 允许multi-line(多线) user input
被Control + Z
结束. 参见User input.
- (for %i in (1,2,3) do @echo %i) > myfile.txt
将整个循环的output重定向到文件.
- for %i in (1,2,3) do @echo %i > myfile.txt
每次进入循环体的时候重新开始重定向, 上一次循环迭代的输出都会丢失.
Redirection at ss64.com
Using command redirection operators at Microsoft
(…)
命令解释器在每次执行完一行或一个 bracketed group(括号组)的时候会reload(重载)batch的内容.
如果开始下面的batch, 在刚开始batch的时候就将 “echo A”改为”echo B”, 输出会是B.
@echo off
ping -n 6 127.0.0.1 >nul & REM wait
echo A
单行中的内容: 把”echo A”放在如下的单行中, 那运行时就不会有变化
@echo off
ping -n 6 127.0.0.1 >nul & echo A
或者放入一个括号围起来的commands中, 在脚本运行后改变”echo A”也没影响.
@echo off
for /L %%i in (1,1,10) do (
echo A
ping -n 2 127.0.0.1 >nul & REM wait
)
类似地:
@echo off
(
ping -n 6 127.0.0.1 >nul & REM wait
echo A
)
command interpreter process(命令解释过程)中的环境变量会被它所执行的其他(external)过程所继承. 少数环境变量是命令解释器自己使用的. 改变它们会改变解释器的操作.
环境变量可以被 SET
, PATH
, PROMPT
命令影响.
要复原(unset)一个变量, 把它设为空string: set mayvar=
命令解释从创建它的process中继承初始的环境变量集. 如果命令解释器是从桌面快捷方式启动的, 那么就会是Windows Explorer.
命令解释器通常是个文本式的用户接口, 没有图形, 因此也无法识别Windows注册表(Registry)中的环境变量模板被修改时通知应用程序的消息.
在控制面板中改变环境变量会引起Windows Explorer从注册表模板中更新自身的环境变量, 这样的改变环境变量会在subsequently invoked(随后调用)的命令解释器中继承. 然而, 它不会引起已经运行中的命令解释器从注册表的模板中更新自身的环境变量.
COMSPEC环境变量包含命令解释器程序文件的全路径名. 这是从parent process(父进程)中继承而来的, 即在注册表环境变量模板中的COMSPEC的设置中间接继承.
PATH环境变量值包含目录名列表, 由分号(semi-colon)分隔. 当定位外部命令所执行的程序文件时, 这个目录列表会依次被查询.
PATHEXT环境变量值包含文件后缀名列表, 由分号分隔. 当定位外部程序所执行的程序文件时, 这个文件后缀名列表会依次被应用.
echo %PATHEXT%
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
添加 “.PL”到变量中, 可以确保即使没有驶入”.pl”后缀名, Perl程序在命令行中页可以执行. 这样, 不用输入”mydiff.pl a.txt b.txt”, 可以直接输入”mydiff a.txt b.txt”.
把”.PL”添加到Windows Vista以及随后版本的变量中:
setx PATHEXT %PATHEXT%;.PL
Windows Environment Variables at ss64
Making Python scripts run on Windows without specifying “.py” extension at stackoverflow
PROMPT环境变量在命令解释器显示prompt(提示符)的时候控制文字的显示.
在交互式模式(interactive mode)下提示一个新的command line时, 或者在batch文件模式下当回显batch文件时, 命令解释器会显示提示符.
[e.g. PROMPT $D ]
在PROMPT环境变量值中各种特殊字符序列会造成各种特殊效果:
Characters | Expansion Result |
---|---|
$$ | $字符自身 |
$A | $符号(ampersand). 便利方式, 因为将&字面值通过SET命令放入PROMPT环境变量的值中很麻烦. |
$B | ‘|’(pipe符号) |
$C | 左括号’(‘ |
$D | 当前日期 |
$E | ESC (ASCII码27) |
$F | 右括号’)’ |
$G | 大于号 ‘>’ |
$H | Backspace(删除之前的字符) |
$L | 小于号 ‘<’ |
$M | 如果是个网络驱动(network drive), 则是link到当前驱动的remote名字, 否则是空string |
$N | 当前驱动盘符(drive letter) |
$P | 当前驱动盘符及其全路径 |
$Q | ‘=’ 等号 |
$S | ’ ’ 空格 |
$T | 当前系统时间 |
$V | Windows版本号 |
$_ | carriage return character(回车) “enter” |
$+ | 和 pushd目录层次中的item一样多的加号 (+) |
prompt at ss64
prompt at Microsoft
多数Windows命令提供switch(开关) – 行为导向的选项(option).
注意:
- siwtch多数是由单个字母构成; 一些siwtch由一系列的多个字母构成
- swicth前置一个斜杠(/), 而有些操作系统中会前置一个减号(-)
- swicth是大小写敏感的, 而有些操作系统则不敏感
- 如果另一个操作系统的命令(如grep)被导入Windows, 它通常是保留原来的选项约定, 包括使用 - 和大小写不敏感.
ex.
- dir /?
显示帮助文档. 多数命令都具备此选项.
- dir /b /s
递归地列出当前文件夹下所有文件和文件夹.
- dir /bs
不工作, swicth无法再单个斜杠后面累加.
- findstr /ric:"id: *[0-9]*" File.txt
和其他命令不同findstr允许在单个斜杠后面累加swicth. 事实上, r, i, c 是单个字母的swicth.
- dir/b/s
可以工作. 在dir中, 把命令和switch之间的whitespace移除, 或者switch之间的whitespace移除不影响命令. 和 dir /b /s
是一样的.
- tree/f/a
不工作. 不像 tree /f /a
. 在tree中, In tree, whitespace分隔是强制的. 不像find/i/v
那样可以工作.
- dir /od
switch字母o会被之后的单个字母修改, 指定由date排序.字母d自身不是个switch字母. 类似的还有dir /ad
和 more /t4
.
- dir /B /S
switch是大小写敏感的, 不像其他操作系统.
- sort /r file.txt
逆序来对文件内容进行排序.
- sort /reverse file.txt
sort允许使用比单个字母长的switch string.
- sort /reve file.txt
效果同上. sort允许使用不完全(substring)的switch名.
- sort /reva file.txt
不工作, “reva” 不是 “reverse”的子串.
- taskkill /im AcroRd32.exe
taskkill需要multiletter(多字母)的 switch名字 – /im, 简写为 /i不行.
- java -version
java是来自另一个操作系统家族的环境, switch(option)使用减号约定.
- grep –help
如果安装了GNU grep, 它需要多字母的switch, 前置两个dash(横杠).
命令通常在执行结束时设置error level. Windows NT以及其后版本, 它是个 32-bit signed integer(有符号整数); MS DOS中, 使用的是一个0~255的整数.
Keywords: return code, exit code, exit status.
error level的约定:
- 0 - sucess
- not 0 - failure
- error level通常是正值
- 如果命令不分辨各种不同的failure, error level的failure值通常是1
error level的使用:
- 可以使用 &&
和 ||
来测试, 参见 Syntax
- 可以使用 IF
来测试
- 值可以从 ERRORLEVEL
变量中获取.
ex.
- dir >NUL && echo Success
&& 后面的部分只有在 error level是 zero的时候才执行.
- color 00 || echo Failure
|| 后面的部分只有在 error是 non-zero的时候才执行, 无论是正数还是负数.
- color 00 || (
echo Failure
)
多个括号组也可以.
- echo %ERRORLEVEL%
显示 error level, 但不会修改它.
- if %errorlevel% equ 0 echo
error level是zero, success
- if %errorlevel% neq 0 echo
error level是 non-zero, failure
- if errorlevel 1 echo
error leve是 >= 1, positive error level的 failure.
- 不涉及negative error level. Note “>=”的部分和 if %errorlevel% equ 1
不同
- exit /b 1
返回一个batch文件, error level设为1.
- cmd /c "exit /b 10"
在batch文件或command line的中间, 设置error level为10.
- (cmd /c "exit /b 0" && Echo Success) & (cmd /c "exit /b -1" || Echo Failure)
和上面一样, 显示的error level实际上是被影响了.
- (cmd /c "exit /b 0" & cmd /c "exit /b 1") || Echo Failure
命令链(chain)中由 &创建的error level是最后一个命令的返回值.
- cmd /c "exit /b -1" & if not errorlevel 1 echo Would-be success
“if not errorlevel 1”测试, 可能是测试success; 可传递负数: 它测试”not error level >=1” 即 “error leve <= 0”
- set myerrorlevel=%errorlevel%
记住error level, 之后使用
- set errorlevel=0
要防止: overshadow(遮住) built-in的 errorlevel变量. 确保随后获取的 %ERRORLEVEL%返回0, 而不是真实的error level.
- cmd /c "exit /b 0"
if 1 equ 1 (cmd /c "exit /b 1" & echo %errorlevel% )
显示0, 因为 %errorlevel%在 cmd /c “exit /b 1” 被执行前先扩展.
Error level at ss64
根据位置和长度获取变量的substring.
在运行下列例子前, 先确保 %a% 和 “abcd”相等:
- set a=abcd
ex.
- echo %a:~0,1%
Result: a
- echo %a:~1,1%
Result: b
- echo %a:~0,2%
Result: ab
- echo %a:~1,2%
- Result: bc
- echo %a:~1%
Result: bcd
- echo %a:~-1%
Result: d
- echo %a:~-2%
Result: cd
- echo %a:~0,-2%
Result: ab
- echo %a:~0,-1%
Result: abc
- echo %a:~1,-1%
Result: bc
测试substring containment:
- if not "%a:bc=%"=="%a%" echo yes
- 如果变量包含”bc”子串, 显示 yes
- 这个测试用了string replacement的技巧
- 如果变量包含引号, 这个测试就无法工作
测试”starts with”:
- if %a:~0,1%==a echo yes
如果变量以”a”开始, 显示 yes
- if %a:~0,2%==ab echo yes
如果变量以”ab”开始, 显示 yes
字符串替换(string replacement):
- set a=abcd & echo %a:c=%
Result: abd
- set a=abcd & echo %a:c=e%
Result: abed
- set a=abcd & echo %a:*c=%
- Result: d
- asterisk(星号) 只有在sought pattern(寻找模式)的开头才有用, 在中间或结尾都没用.
参见 SET 命令 set /?
将string用 ” ” “,” “;”拆分开 (space, comma, semicolon
set myvar=a b,c;d
for %%a in (%myvar%) do echo %%a
用分号拆分string, 假设字符串没有包含引号:
@echo off
set myvar=a b;c;d
set strippedvar=%myvar%
:repeat
for /f "delims=;" %%a in ("%strippedvar%") do echo %%a
set prestrippedvar=%strippedvar%
set strippedvar=%strippedvar:*;=%
if not "%prestrippedvar:;=%"=="%prestrippedvar%" goto :repeat
—TBC—
—YCR—