此代码
@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
为什么差异以及如何获得预期效果?
答案 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=content
比set 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
设置为该值。
我不知道这是否是关于标准和延迟扩展应该如何工作的记录行为,或者只是一个实现细节(这意味着它将来可能会中断)