为什么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 

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

问题是set myvar=!myvar! 扩展set myvar=first
你把它设置为相同的内容,然后你问echo myvar:!myvar! 显示myvar的内容。

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

两个都显示了!%var%!的双重扩展!%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 

我希望有一点有趣
如果你想更深入的了解,你可以阅读致电我,或者更好地避免打电话
和Windows命令解释器(CMD.EXE)如何解析脚本?

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

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

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

先前的方法可以用来进行第三层甚至更深层次的扩展,甚至可以与延迟扩展结合来创建非常复杂的价值管理。 这件事不只是好奇心,而是访问数组元素或链表的关键。 例如:

 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 

你正在做的事情将无法工作 – 延迟扩展只会改变块内变量的变量扩展行为。 它不允许你尝试的别名/嵌套(缺少一个更好的单词)。

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

我不相信有什么可以完成你在这里做的。


*编辑*

确定后,看看你的答案,我看到这是如何工作的,你可以得到你想要的输出:

 @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

我不知道这是否是标准的和延迟的扩展应该如何工作的文档化行为,或者只是一个实现细节(这意味着它可能会在未来破裂)