为什么批处理文件中的延迟扩展在这种情况下不起作用?

时间:2011-10-24 21:46:55

标签: windows batch-file

此代码

@echo off
setlocal EnableDelayedExpansion

set myvar=first
set first=second

echo myvar:!myvar!
set myvar=!myvar!
echo myvar:!myvar!

给出

myvar:first
myvar:first

在Windows Vista SP2上。

我预期的输出是

myvar:first
myvar:second

为什么差异以及如何获得预期效果?

3 个答案:

答案 0 :(得分:9)

问题是set myvar=!myvar!扩展为set myvar=first
您使用相同的内容设置它,然后您要求echo myvar:!myvar!显示myvar的内容。

我会尝试添加更多解释,即使Aacini和shf301已经回答了这个问题。

两者都展示了!%var%!构造的双重扩展,Aacini解释了它为什么可以工作,以及反转版%!var!%无法工作的原因。

恕我直言,有四种不同的扩展 延迟扩展
正如Aacini所解释的那样,延迟扩展对内容中的任何特殊字符都是安全的(它可以处理从0x01到0xFF的所有字符)。

扩展百分比:
扩展百分比无法处理或删除某些字符(即使转义) 它对简单内容很有用,因为它可以在endlocal障碍之后扩展变量。

setlocal
set "myVar=simple content"
(
endlocal
set result=%myVar%
)

FOR-Loop-Parameters扩展:
如果禁用延迟扩展,则是安全的,否则延迟扩展阶段在%% a变量扩展后执行 它可能很有用,因为它可以在endlocal障碍

之后扩展变量
setlocal EnableDelayedExpansion
set "var=complex content &<>!"
for /F "delims=" %%A in ("!var!") DO (
  endlocal
  set "result=%%A"
)

SET扩展:
set var也扩展了一个变量,它始终是安全的,并且独立于延迟扩展模式。

Aacini刚刚解释了call %%%var%%%构造的工作方式,我只想补充一些内容 call是可堆叠的,您可以使用其中的许多,并且每个都重新启动解析器。

set "var=%%var%%#"
call call call call call echo %var%

结果为%var%######

call有许多缺点/副作用! 每次调用加倍所有插入符号^
你可以说:“嘿,我已经测试了它,我看不到任何加倍”

call call call call echo ^^

结果^

然而这是真的,但它主要是隐藏的,因为每次重启都有一个特殊的角色阶段,其中插入符号逃脱下一个角色,但你可以看到加倍效果

call call call call echo "^^"^^

结果"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"^

即使呼叫扩展重新启动解析器,也绝不能在任何阶段使用延迟扩展(仅在第一阶段)。

如果检测到未转义的特殊字符,

call将停止工作。

echo you ^& me
call echo you & me
call echo you ^& me
call echo you ^^& me
call echo you ^^^& me

只有第一个结果输出到you & me,其他所有结果都失败了。

另一个问题是呼叫速度极慢,call set var=contentset var=content慢50倍,原因是呼叫尝试启动外部程序。

@echo off
setlocal
(
   echo echo *** External batch, parameters are '%%*'
) > set.bat
set "var="
call set var=hello
set var

我希望它有点有趣......
如果你想深入了解,可以阅读CALL me, or better avoid call
How does the Windows Command Interpreter (CMD.EXE) parse scripts?

答案 1 :(得分:2)

此问题与延迟变量扩展没有直接关系,而是需要两个值扩展:第一个提供变量名称,第二个必须用其值替换此名称。直接的方法是通过前面答案中显示的同一行中的两个扩展:set myvar=!%myvar%!,因为%var%扩展在分析命令行执行之前完成,而!var!稍后在命令执行之前完成扩展(因此称为“延迟”名称)。这意味着%var%扩展可能会提供命令的一部分,并可能导致语法错误,但是!var!不。例如,如果var为空或有空格,if %var%==value ...会导致错误,但if !var!==value ...永远不会导致语法错误。

可以通过不涉及延迟变量扩展的其他方式实现值的双重扩展。例如,我们可以创建一个辅助批处理文件来执行第二次扩展:

echo myvar:%myvar%
echo set myvar=%%%myvar%%%> auxiliary.bat
call auxiliary
echo myvar:%myvar%

以前的方法可用于进行第三次甚至更深层次的扩展,甚至可以与Delayed Expansions结合使用来创建非常复杂的价值管理。这个问题不仅仅是一种好奇心,而是访问数组元素或链表的关键。例如:

set month[1]=January
set month[2]=February
. . .
set month[12]=December
for /f "tokens=1-3 delims=/" %%a in ("%date%") do echo Today is !month[%%a]! %%b, %%c

答案 2 :(得分:1)

您尝试做的事情不起作用 - 延迟扩展只会更改块内变量的变量扩展行为。它不允许您尝试使用别名/嵌套(缺少更好的单词)。

set myvar=first将变量myvar设置为文本“first”。 set first=second首先将变量设置为文本“second。这两行之间没有链接。myvar永远不会评估为未明确设置的内容。

我不相信无论如何都要完成你想要做的事情。


*编辑*

看了你的answer我看到它是如何工作的,你可以用这个得到你想要的输出:

@echo off
setlocal EnableDelayedExpansion

set myvar=first
set first=second

echo myvar:%myvar%
set myvar=!%myvar%!
echo myvar:%myvar%

因此,由于标准和延迟扩展的发生方式,似乎发生了魔术。行set myvar=!%myvar%!似乎首先由标准扩展器扩展为set myvar=!first!(如果您使用echo on运行脚本,则会看到这一点)。然后延迟扩展器运行并将!first扩展为“秒”并将myvar设置为该值。

我不知道这是否是关于标准和延迟扩展应该如何工作的记录行为,或者只是一个实现细节(这意味着它将来可能会中断)