Bro*_*own 82 windows batch command-line
我有两个非常简单的批处理文件示例:
给变量赋值:
@echo off
set FOO=1
echo FOO: %FOO%
pause
echo on
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,结果是:
FOO: 1
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)
但是,如果我将相同的两行放在 IF NOT DEFINED 块中:
@echo off
IF NOT DEFINED BAR (
set FOO=1
echo FOO: %FOO%
)
pause
echo on
Run Code Online (Sandbox Code Playgroud)
这出乎意料地导致:
FOO:
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)
这应该与 IF没有任何关系,显然该块正在被执行。如果我在 if 上方定义 BAR,则只显示 PAUSE 命令中的文本,正如预期的那样。
是什么赋予了?
跟进问题:有没有办法在没有 setlocal 的情况下启用延迟扩展?
如果我从另一个内部调用这个简单的示例批处理文件,则该示例设置 FOO,但仅限于 LOCALLY。
例如:
测试调用者.bat
@call test.bat
@echo FOO: %FOO%
@pause
Run Code Online (Sandbox Code Playgroud)
测试.bat
@setlocal EnableDelayedExpansion
@IF NOT DEFINED BAR (
@set FOO=1
@echo FOO: !FOO!
)
Run Code Online (Sandbox Code Playgroud)
这显示:
FOO: 1
FOO:
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)
在这种情况下,看来我必须在 CALLER 中启用延迟扩展,这可能很麻烦。
Joe*_*oey 99
解析一行时,批处理文件中的环境变量会被扩展。对于由括号分隔的块(作为您的if defined
),整个块算作“行”或命令。
这意味着在块运行之前,所有出现的 %FOO% 都被它们的值替换。在您什么都没有的情况下,因为变量还没有值。
要解决这个问题,您可以启用延迟扩展:
setlocal enabledelayedexpansion
Run Code Online (Sandbox Code Playgroud)
延迟扩展会导致由感叹号 ( !
)分隔的变量在执行时被评估而不是解析,这将确保您的情况下的正确行为:
if not defined BAR (
set FOO=1
echo Foo: !FOO!
)
Run Code Online (Sandbox Code Playgroud)
help set
也详细说明了这一点:
最后,增加了对延迟环境变量扩展的支持。默认情况下始终禁用此支持,但可以通过
/V
命令行开关启用/禁用CMD.EXE
. 看CMD /?
延迟环境变量扩展对于绕过当前扩展的限制非常有用,该限制发生在读取一行文本时,而不是执行时。以下示例演示了立即变量扩展的问题:
Run Code Online (Sandbox Code Playgroud)set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
永远不会显示消息,因为
%VAR%
在读取第一个 IF 语句时,两个IF
语句中的the都会被替换,因为它在逻辑上包含了 的主体IF
,这是一个复合语句。所以复合语句中的 IF 实际上是在比较“before”和“after”,后者永远不会相等。同样,以下示例也不会按预期工作:Run Code Online (Sandbox Code Playgroud)set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
因为它不会在当前目录中建立文件列表,而只会将
LIST
变量设置为找到的最后一个文件。同样,这是因为在 读取语句%LIST%
时只扩展了一次FOR
,并且此时LIST
变量为空。所以我们正在执行的实际 FOR 循环是:Run Code Online (Sandbox Code Playgroud)for %i in (*) do set LIST= %i
它只是保持设置
LIST
为找到的最后一个文件。延迟环境变量扩展允许您在执行时使用不同的字符(感叹号)来扩展环境变量。如果启用了延迟变量扩展,则上述示例可以编写如下以按预期工作:
Run Code Online (Sandbox Code Playgroud)set VAR=before if "%VAR%" == "before" ( set VAR=after if "!VAR!" == "after" @echo If you see this, it worked ) set LIST= for %i in (*) do set LIST=!LIST! %i echo %LIST%
当命令位于单行时(&
是命令分隔符)也会发生相同的行为:
if not defined BAR set FOO=1& echo FOO: %FOO%
Run Code Online (Sandbox Code Playgroud)
乔伊的解释是我最喜欢的。但请注意,这enabledelayedexpansion
在 Windows NT 4.0 上不起作用(我不确定 Windows 2000 是否也如此)。
关于您的后续问题,不,没有 是不可能EnableDelayedExpansion
的setlocal
。然而,不利于您的原始行为可以用来解决第二个问题:技巧是endlocal
在同一行中再次设置所需变量的值。
这是您test.bat
修改后的:
@echo off
setlocal EnableDelayedExpansion
IF NOT DEFINED BAR (
set FOO=1
echo FOO: !FOO!
)
endlocal & set FOO=%FOO%
Run Code Online (Sandbox Code Playgroud)
但这里有另一个解决该问题的方法:使用同一文件中的过程而不是内联块或外部文件。
@echo off
if not defined BAR call :NotDefined
pause
goto :EOF
:NotDefined
set FOO=1
echo FOO: %FOO%
goto :EOF
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
144873 次 |
最近记录: |