Windows的批处理(bat)脚本

之前只用过Linux的shell脚本,从来没有用过Windows的bat脚本。最近有项工作,需要用到bat脚本,于是简单学习和总结一下。初次接触,一些地方估计有错误和遗漏,有待继续深入研究。

环境

  • Windows 11 家庭中文版

指令

在D盘根目录下创建 test1214.bat 文件。打开CMD窗口,在D盘根目录下,通过 test1214.bat 命令运行脚本。

pause

顾名思义,暂停脚本,按任意键继续。如果是双击运行脚本,该命令非常有用,否则窗口一闪就没了。

pause

运行结果如下:

D:\>test1214.bat

D:\>pause
请按任意键继续. . .

echo

类似Linux的echo命令。比如:

echo "hello world"

运行结果如下:

D:\>test1214.bat

D:\>echo "hello world"
"hello world"

注意:不需要加双引号,如果加上双引号,则双引号也会当做字符串的一部分显示出来。但是如果字符串中包含特殊字符,比如 > ,就需要加上双引号。

注意:bat脚本默认是回显脚本内容的。如果运行时不想回显脚本内容,则可用 echo off 命令关闭回显。

echo off

echo "hello world"

运行结果如下:

D:\>test1214.bat

D:\>echo off
"hello world"

注:此时 echo off 本身还是会回显,可用 @echo off 来避免回显本行。

下面的例子都用 @echo off 来关闭回显以及其本身。

如果 echo 后面不跟参数,则表示查询当前的echo状态是打开还是关闭。

@echo off

echo

运行结果如下:

D:\>test1214.bat
ECHO 处于关闭状态。

注意:如果echo一个变量,而该变量当前没有设值,则变成显示echo状态了。

@echo off

set x=

echo %x%

运行结果如下:

D:\>test1214.bat
ECHO 处于关闭状态。

echo 命令还可用于换行:

  • echo.
  • echo\
@echo off

echo aaa

echo.

echo bbb

echo\

echo ccc

运行结果如下:

D:\>test1214.bat
aaa

bbb

ccc

@

前面已提到,其用于不回显本行内容。

@echo off

echo "hello world"

运行结果如下:

D:\>test1214.bat
"hello world"

rem

注释本行。

@echo off

echo aaa

rem echo bbb

echo ccc

运行结果如下:

D:\>test1214.bat
aaa
ccc

call

调用另一个脚本。

@echo off

echo "hello world"

call a.bat

echo "end"

文件 a.bat 内容如下:

@echo off

echo "I am A"

运行结果如下:

D:\>test1214.bat
"hello world"
"I am A"
"end"

如果没有 call ,直接调用 a.bat ,则后者运行结束之后,前者也随之结束,后面的脚本不再运行。(为何如此设计?)

@echo off

echo "hello world"

a.bat

echo "end"

运行结果如下:

D:\>test1214.bat
"hello world"
"I am A"

可见, echo "end" 这一行并没有运行。

call 命令也可用于调用一个标签,就不用 goto 过去了。注意标签处要用 exit /b 返回。详见最下面的例子。

start

新开一个窗口,异步运行另一个脚本文件。

@echo off

echo "hello world"

start a.bat

echo "end"

运行结果如下:

D:\>test1214.bat
"hello world"
"end"

注意会新开一个窗口来运行 a.bat ,并且是异步运行的,不会等其结束, echo "end" 就会立即运行。

如果要同步运行(等待其运行结束),则需要加上 /wait 选项:

start /wait a.bat

则第二个窗口关闭(不是被调用的脚本运行结束)之后,第一个脚本才继续运行。

使用 /b 选项可以不打开新窗口:

@echo off

echo "hello world"

start /b a.bat

echo "end"

运行结果如下:

D:\>test1214.bat
"hello world"
"end"

D:\>"I am A"

虽然没有打开新窗口,貌似还是打开了一个新shell,因为此时可以用 exit 退回到上一层的shell。

%0 到 %9

脚本的命令行参数。注意 %0 代表的是脚本本身。

@echo off

echo "hello world"

echo %0
echo %1
echo %2
echo %3

echo "end"

运行结果如下:

D:\>test1214.bat aaa bbb
"hello world"
test1214.bat
aaa
bbb
ECHO 处于关闭状态。
"end"

因为 %3 没有值,所以相当于查看 echo 状态,显示 ECHO 处于关闭状态

choice

让用户选择一个选项,最常见的就是选择 Y 或者 N

@echo off

choice

运行结果如下:

D:\>test1214.bat
[Y,N]?

此时,用户需要输入 Y 或者 N (不区分大小写),比如:

D:\>test1214.bat
[Y,N]?Y

用户的选项会保存在环境变量 errorlevel 里。第1个选项值为1,第2个选项值为2,因此类推。

注意:不要显式设置 errorlevel 变量的值!否则 errorlevel 变量会一直是这个值!如果已经设置了,则可以用 set errorlevel= 来清除其值,就OK了。

@echo off

choice

echo You choice is: %errorlevel%

运行脚本,并输入 Y ,如下:

D:\>test1214.bat
[Y,N]?Y
You choice is: 1

可以通过 /c 选项来定制可选值,并通过 /m 提供一个消息,比如:

@echo off

choice /c XYZ /m "You must make a choice:"

echo You choice is: %errorlevel%

运行脚本,并输入 Y ,如下:

D:\>test1214.bat
You must make a choice: [X,Y,Z]?Y
You choice is: 2

还可以使用 /t 倒计时,并通过 /d 提供默认选项。

@echo off

choice /t 5 /d y

echo You choice is: %errorlevel%

运行脚本,不需要输入,等待5秒,脚本就会自动帮你选择 y

D:\>test1214.bat
[Y,N]?Y
You choice is: 1

注:可用 choice /? 查看 choice 的帮助信息。

set

设置变量。使用变量时需要用 % 包起来,例如:

@echo off

set x=good

echo %x%

注意:等号两边不要有空格!如果左边有空格,则实际上并没有赋值。

运行结果如下:

D:\>test1214.bat
good

注意:在脚本里set的变量,在脚本运行结束后仍然生效:

D:\>echo %x%
good

当然在下次运行脚本时,也仍然生效(在同一个窗口运行脚本)。

这一点是和Linux脚本不同的。Linux脚本的变量,默认只在子shell(运行时会开启一个子shell)中生效。当然,可以用 source (即 . )命令使之在当前shell中运行。

这个特点在调试bat脚本时,经常带来很大的困扰,比如实际并没有set进去,但是echo其值时,还能显示出早前set的值,导致我以为等号左边可以加空格。

清除变量,用 set x= 即可。

判断变量是否为空值,可用 "%x%" == "" 来判断。

@echo off

set x=aaa

if "%x%" == "" (echo x is null ) else (echo x is not null )

set x=

if "%x%" == "" (echo x is null ) else (echo x is not null )

运行结果如下:

D:\>test1214.bat
x is not null
x is null

/p 选项用来接收用户输入:

@echo off

set /p x="Please input a value:"

echo The value is: %x%

运行脚本,并输入 haha

D:\>test1214.bat
Please input a value:haha
The value is: haha

注意 choiceset /p都可以和用户交互,前者是提供选项,后者则是任意输入。

/a 选项会把右边先求值,再赋值:

@echo off

set x=1

set y=2

set /a z=%x% + %y% + 3

echo %z%

运行结果如下:

D:\>test1214.bat
6

if

直接看例子:

@echo off

choice /c XYZ /m "You must make a choice:"

echo %errorlevel%

if %errorlevel% == 1 (
	echo XXX ) else if %errorlevel% == 2 (
	echo YYY ) else if %errorlevel% == 3 (
	echo ZZZ ) else (echo Something is wrong )

注意格式,比如哪里可以换行,再比如括号两边的空格。如果格式不对脚本会出问题。

运行脚本,输入 z ,如下:

D:\>test1214.bat
You must make a choice: [X,Y,Z]?Z
3
ZZZ

/i 选项表示不区分大小写,比如:

@echo off

set /p x="Please input a value:"

echo %x%

if /i %x% == aaa (echo yes ) else (echo no )

运行脚本,输入 aAa ,则会打印 yes

D:\>test1214.bat
Please input a value:aAa
aAa
yes

同理,输入 AAAaaa 等,效果一样的。

如果没有 /i ,则只能匹配 aaa

常见的关系操作:

  • equ :相等
  • neq :不等
  • gtr :大于
  • geq :大于等于
  • lss :小于
  • leq :小于等于
@echo off

set x=123

if x equ 100 (echo "x = 100 yes" ) else (echo "x = 100 no" )

if x neq 100 (echo "x != 100 yes" ) else (echo "x != 100 no" )

if x gtr 100 (echo "x > 100 yes" ) else (echo "x > 100 no" )

if x geq 100 (echo "x >= 100 yes" ) else (echo "x >= 100 no" )

if x lss 100 (echo "x < 100 yes" ) else (echo "x < 100 no" )

if x leq 100 (echo "x <= 100 yes" ) else (echo "x <= 100 no" )

运行结果如下:

D:\>test1214.bat
"x = 100 no"
"x != 100 yes"
"x > 100 yes"
"x >= 100 yes"
"x < 100 no"
"x <= 100 no"

not 表示“非”,貌似没有“与”和“或”。

@echo off

set x=123

if not x gtr 100 (echo "not x > 100 yes" ) else (echo "not x > 100 no" )

运行结果如下:

D:\>test1214.bat
"not x > 100 no"

有优先级吗?有待研究。

for

直接看例子:

@echo off

set list=a b c d e

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

运行结果如下:

D:\>test1214.bat
a
b
c
d
e

特别要小心的是赋值。

@echo off

set list=a b c d e

set x=hello

for %%i in (%list%) do (
	echo %%i
	set x=%%i
	echo %x%
)

运行结果如下:

D:\>test1214.bat
a
hello
b
hello
c
hello
d
hello
e
hello

set x=%%i 貌似并没有生效,这是为什么呢?

我也不太明白,总之如果想要这么做,需要先设置一个参数: setlocal enabledelayedexpansion ,同时要用 ! 把变量包起来。

@echo off

set list=a b c d e

set x=hello

setlocal enabledelayedexpansion

for %%i in (%list%) do (
	echo %%i
	set x=%%i
	echo !x!
	echo %x%
)

运行结果如下:

D:\>test1214.bat
a
a
hello
b
b
hello
c
c
hello
d
d
hello
e
e
hello

可见, echo !x! 打印的是新值,而 echo %x% 打印的还是旧值,不知道为什么,有待研究。

goto

goto 和标签一起使用。

@echo off

echo start

set x=0

:label1

echo %x%

set /a x= %x% + 1

if %x% lss 5 ( goto label1 )

echo end

运行结果如下:

D:\>test1214.bat
start
0
1
2
3
4
end

exit

不要直接 exit ,否则会连同CMD窗口一起关闭。

常见做法是加上 /b 选项,可以传递一个返回值(用 errorlevel 变量接收)。

@echo off

echo hello

exit /b 1

运行结果如下:

D:\>test1214.bat
hello

此时查看 errorlevel 变量:

D:\>echo %errorlevel%
1

也可以不设置返回值,直接 exit /b 返回,则 errorlevel 变量的值不受影响。

下面的例子是简单的“结构化”的小脚本:

@echo off

echo Start

choice /c AB /m "Choose A or B:"

if %errorlevel% == 1 (
	echo Going to labela
	goto labela ) else if %errorlevel% == 2 (
	echo Going to labelb
	goto labelb ) else (
	echo Something is wrong
	exit /b 1
	)

:labelend
echo End
exit /b

:labela
echo A
rem do something
goto labelend

:labelb
echo B
rem do something
goto labelend

运行脚本,输入 a ,如下:

D:\>test1214.bat
Start
Choose A or B: [A,B]?A
Going to labela
A
End

运行脚本,输入 b ,如下:

D:\>test1214.bat
Start
Choose A or B: [A,B]?B
Going to labelb
B
End

例子

下面是一个完整的小例子。该脚本对2个list做处理。

@echo off

echo Start
echo.

set list1=a b c

set list2=x y z

echo Handling list1

setlocal enabledelayedexpansion

for %%i in (%list1%) do (
	echo Looping %%i start
	set x=%%i
	call :label1
	echo Looping %%i end
)

echo.

echo Handling list2

for %%i in (%list2%) do (
	echo Looping %%i start
	set x=%%i
	call :label2
	echo Looping %%i end
)

echo.
echo End

exit /b

:label1
echo Handling !x! in label1
rem do something
echo Done !x! in label1

exit /b

:label2
echo Handling !x! in label2
rem do something
echo Done !x! in label2

exit /b

运行结果如下:

D:\>test1214.bat
Start

Handling list1
Looping a start
Handling a in label1
Done a in label1
Looping a end
Looping b start
Handling b in label1
Done b in label1
Looping b end
Looping c start
Handling c in label1
Done c in label1
Looping c end

Handling list2
Looping x start
Handling x in label2
Done x in label2
Looping x end
Looping y start
Handling y in label2
Done y in label2
Looping y end
Looping z start
Handling z in label2
Done z in label2
Looping z end

End

你可能感兴趣的:(windows,bat,脚本)