我在批处理文件中有以下代码:
set "start=2"
FOR /L %%a in (1,1,3) DO (
FOR /L %%b in (%start%,1,3) DO (
echo %start% Before Call
call :changestring
FOR /L %%c in (1,1,3) DO (
REM Nothing
)
)
)
pause
Goto :Eof
:changestring
echo %start% Before Set
set "start=1"
echo %start% After Set
pause
goto :eof
我想要的是变量start
的值在第二个 FOR 循环中引用两次,%start%
在运行后从2
更改为1
子程序changestring
,命令行为call :changestring
。
环境变量start
的值在子例程中从2
修改为1
,作为子例程中两个echo
命令行的输出。但在第二个 FOR 循环中,该值始终为2
且不会更改。
如何在第二个 FOR 循环中修改环境变量start
的值?
答案 0 :(得分:1)
在运行批处理文件时,首先执行pause
,如同在命令提示符窗口中发布的那样,没有通常在批处理文件顶部使用的@echo off
{{3} }}:
set "start=2"
FOR /L %a in (1 1 3) DO (FOR /L %b in (2 1 3) DO (
echo 2 Before Call
call :changestring
FOR /L %c in (1 1 3) DO (REM Nothing )
) )
(FOR /L %b in (2 1 3) DO (
echo 2 Before Call
call :changestring
FOR /L %c in (1 1 3) DO (REM Nothing )
) )
(
echo 2 Before Call
call :changestring
FOR /L %c in (1 1 3) DO (REM Nothing )
)
2 Before Call
echo 2 Before Set
2 Before Set
set "start=1"
echo 1 After Set
1 After Set
pause
Windows命令解释cmd.exe
解释批处理文件并执行命令行输出,而不执行echo off
命令行,在执行预处理/解析之后始终执行命令行,如{ {3}}
可以看出,在执行最外面的 FOR 命令之前,Windows命令解释器完全预处理以(
开头并以匹配)
结尾的命令块时间。在通过引用的环境变量的当前值%start%
预处理整个命令块期间,两个2
的出现都被替换。因此, FOR 几次使用的整个命令块不再包含环境变量引用。只有循环变量引用保留在命令块中。
命令提示符窗口中运行set /?
时命令 SET 输出的帮助描述了在预处理期间已经评估%variable%
所做的所有环境变量引用的问题。 SET 的帮助还描述了 IF 条件和 FOR 循环的解决方案,其中环境变量的当前值在命令中发生变化阻止或在循环执行期间:使用debug this batch file。
因此,让我们按照How does the Windows Command Interpreter (CMD.EXE) parse scripts?中的说明使用命令 SETLOCAL 和 ENDLOCAL 来启用延迟环境变量扩展以及命令扩展默认情况下已启用,此处也需要。
setlocal EnableExtensions EnableDelayedExpansion
set "start=2"
FOR /L %%a in (1,1,3) DO (
FOR /L %%b in (!start!,1,3) DO (
echo !start! Before Call
call :changestring
FOR /L %%c in (1,1,3) DO (
REM Nothing
)
)
)
endlocal
pause
goto :EOF
:changestring
echo %start% Before Set
set "start=1"
echo %start% After Set
pause
goto :EOF
环境变量start
现在在第二个 FOR 命令行及其命令块中使用!start!
引用两次,这意味着在第一行修改时启用了延迟扩展批处理文件。
Windows命令解释程序在运行此修改后的批处理文件时最多输出pause
:
setlocal EnableExtensions EnableDelayedExpansion
set "start=2"
FOR /L %a in (1 1 3) DO (FOR /L %b in (!start! 1 3) DO (
echo !start! Before Call
call :changestring
FOR /L %c in (1 1 3) DO (REM Nothing )
) )
(FOR /L %b in (!start! 1 3) DO (
echo !start! Before Call
call :changestring
FOR /L %c in (1 1 3) DO (REM Nothing )
) )
(
echo !start! Before Call
call :changestring
FOR /L %c in (1 1 3) DO (REM Nothing )
)
2 Before Call
echo 2 Before Set
2 Before Set
set "start=1"
echo 1 After Set
1 After Set
pause
预处理后,!start!
都保留在命令块中未修改。所以现在它确实引用了环路变量start
的当前值,它在执行循环期间被修改。
由于%start%
命令行都不在命令块中,所以不需要在子例程中用!start!
替换echo
,因此每个命令行都是预处理的在紧接执行命令 ECHO 之前用环境变量的当前值替换%start%
。
我希望在预处理/解析单个命令行或整个命令块期间,立即和延迟扩展环境变量之间的区别现在很明显。
顺便说一下:变量的名称start
不好。 start
可以用作环境变量名称,但也可以在同一个批处理文件中使用命令 START ,这样就很难区分环境变量start
和命令start
。更好的是作为变量名称,例如FirstNumber
。
使用FirstNumber
代替start
的优化代码,使用算术表达式减少数量,从而可以在不使用的情况下引用环境变量%
或!
,但在算术表达式的评估期间使用环境变量的当前值。以2
开头的第一个数字应该永远不会减少到低于1的值,这是二进制的原因,或者是1
。
@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FirstNumber=2"
FOR /L %%a in (1,1,3) DO (
FOR /L %%b in (!FirstNumber!,1,3) DO (
echo !FirstNumber! before decrement
set /A "FirstNumber=FirstNumber - 1 | 1"
FOR /L %%c in (1,1,3) DO REM Nothing
)
)
endlocal
运行此批处理文件时,@echo off
位于顶部的输出是:
2 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement
当然set /A "FirstNumber=FirstNumber - 1 | 1"
在这里只是环境变量赋值的标准字符串,即set "FirstNumber=1"
。但我想证明环境变量只能通过算术表达式中的名称来引用,以便在评估表达式时使用环境变量的当前值。命令 SET 。
要了解使用的命令及其工作原理,请打开命令提示符窗口,执行以下命令,并完全阅读为每个命令显示的所有帮助页面。
call /?
echo /?
endlocal /?
for /?
goto /?
pause /?
rem /?
set /?
setlocal /?
结论:仔细阅读命令,应用程序或函数的帮助或文档,对于程序员或脚本编写者来说,总是很好的做法,以避免编写代码时出现问题。