昨天在使用批处理中的 for 语句的时候,最初我采用下面这种写法:
经过查阅资料,我找到了原因:
1、 批处理是解释型语言,脚本不会被编译。在运行时,脚本中所引用的 变量会被简单替换成对应的文本(在批处理中称为 变量扩展,Expansion),这一点可以用 C语言中的宏来理解。
2、批处理中对于变量的赋值(将变量进行扩展) 默认发生在第一次读取到一条完整的命令之后,注意, 命令仅仅只是被读取而 还没有被执行。
明白了这 2 点,上面脚本运行的错误也就解释得通了。
对于 for 语句而言,一条完整命令的形式是 for /arg %%var in (SET) do (COMMAND) 这样的, 包括了 do 之后括号里的所有命令,所以他们只会被读取而不会被执行,自然变量 ip 也就不会被赋值了。在上文脚本中,命令 for /l %%i in (1,1,5) do (set ip=%ip%.%%i ...) 第一次被系统完整读取到时,变量 ip 的值为 192.168.1,所以变量 ip 的打印结果一直是 192.168.1
幸好,微软的工程师在开发解释器时已经发现了这一点,并为我们提供了 setlocal 命令用于 改变解释器的规则。
将上文脚本更改成下面的写法:
使用 setlocal EnableDelayedExpansion 命令后,可以让解释器在每次循环的执行过程中都对变量 ip 进行一次赋值。这种 循环体中为变量多次赋值的操作在批处理中 叫做 对变量进行延时扩展(Delayed Expansion)。
再来看 2 个例子,搞清楚使用感叹号 ! 和百分号 % 的区别:
【例 1】
【例 2】
有关 延时扩展变量 的用法,不知道现在你懂了没有?
@echo off
set ip=192.168.1
for /l %%i in (1,1,5) do (
set ip=%ip%.%%i :: 遍历 IP 池
echo ip=%ip% i=%%i
)
图1
运行结果如上图,显示变量 ip 的值始终为 192.168.1,并没有按预期递增输出 IP。
经过查阅资料,我找到了原因:
1、 批处理是解释型语言,脚本不会被编译。在运行时,脚本中所引用的 变量会被简单替换成对应的文本(在批处理中称为 变量扩展,Expansion),这一点可以用 C语言中的宏来理解。
2、批处理中对于变量的赋值(将变量进行扩展) 默认发生在第一次读取到一条完整的命令之后,注意, 命令仅仅只是被读取而 还没有被执行。
明白了这 2 点,上面脚本运行的错误也就解释得通了。
对于 for 语句而言,一条完整命令的形式是 for /arg %%var in (SET) do (COMMAND) 这样的, 包括了 do 之后括号里的所有命令,所以他们只会被读取而不会被执行,自然变量 ip 也就不会被赋值了。在上文脚本中,命令 for /l %%i in (1,1,5) do (set ip=%ip%.%%i ...) 第一次被系统完整读取到时,变量 ip 的值为 192.168.1,所以变量 ip 的打印结果一直是 192.168.1
幸好,微软的工程师在开发解释器时已经发现了这一点,并为我们提供了 setlocal 命令用于 改变解释器的规则。
将上文脚本更改成下面的写法:
@echo off
setlocal EnableDelayedExpansion
set ip=192.168.1
for /l %%i in (1,1,5) do (
set ip=%ip%.%%i
echo ip=!ip! i=%%i
)
:: 写成 enabledelayedexpansion 也是可以的,不区分大小写
:: 必须使用英文感叹号 ! 而不是百分号来引用需要延时扩展的变量,!ip!
图2
这次的运行结果从上图可以看到,输出和计划一致。
使用 setlocal EnableDelayedExpansion 命令后,可以让解释器在每次循环的执行过程中都对变量 ip 进行一次赋值。这种 循环体中为变量多次赋值的操作在批处理中 叫做 对变量进行延时扩展(Delayed Expansion)。
再来看 2 个例子,搞清楚使用感叹号 ! 和百分号 % 的区别:
【例 1】
@echo off
setlocal enabledelayedexpansion
echo 开始搜索文件,请等待程序提示“搜索完成”再退出 ...
echo.
for /r /d %%i in (.) do (
dir %%i *.xls* 2>nul | find /i "xls"
if %errorlevel% equ 0 ( :: 此处使用百分号 %
echo 文件位置 %%i
)
)
echo.
echo 搜索完成!回车可退出。
pause >nul
图3
【例 2】
@echo off
setlocal enabledelayedexpansion
echo 开始搜索文件,请等待程序提示“搜索完成”再退出 ...
echo.
for /r /d %%i in (.) do (
dir %%i *.xls* 2>nul | find /i "xls"
if !errorlevel! equ 0 ( :: 此处使用感叹号 !
echo 文件位置 %%i
)
)
echo.
echo 搜索完成!回车可退出。
pause >nul
图4
执行结果的对比很明显。
使用百分号 % 时,if 条件判断语句中的 errorlevel 值表示第一次读取完整 for 命令时的值,也就是执行完 echo. 命令后的 errorlevel(有点绕,不着急,好好想想)。命令 echo. 执行成功,所以 errorlevel 一直都是 0,这导致输出结果中打印了很多无用的信息。
使用感叹号 ! 时,if 条件判断语句中的 errorlevel 值是 for 循环中每次执行完 find 命令后的 errorlevel 值,这才是我们计划中要用的。
有关 延时扩展变量 的用法,不知道现在你懂了没有?