windows 为什么在这种情况下批处理文件中的延迟扩展不起作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7882395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is delayed expansion in a batch file not working in this case?
提问by Piotr Dobrogost
This code
这段代码
@echo off
setlocal EnableDelayedExpansion
set myvar=first
set first=second
echo myvar:!myvar!
set myvar=!myvar!
echo myvar:!myvar!
gives
给
myvar:first
myvar:first
on Windows Vista SP2.
在 Windows Vista SP2 上。
The output I had expected is
我预期的输出是
myvar:first
myvar:second
Why the difference and how to obtain desired effect?
为什么会有差异以及如何获得预期的效果?
回答by jeb
The problem is that set myvar=!myvar!
expands to set myvar=first
,
you set it with the same content, and then you ask echo myvar:!myvar!
to show the content of myvar.
问题是set myvar=!myvar!
扩展到set myvar=first
,
你用相同的内容设置它,然后你要求echo myvar:!myvar!
显示 myvar 的内容。
I will try to add some more explanations, even if Aacini and shf301 already answered the question.
我会尝试添加更多解释,即使 Aacini 和 shf301 已经回答了这个问题。
Both showed the double expansion with the !%var%!
construct, and Aacini explained why it can work, and why the reversed version %!var!%
can't work.
两者都展示了!%var%!
构造的双重扩展,Aacini 解释了为什么它可以工作,以及为什么相反的版本%!var!%
不能工作。
IMHO there are four different expansions.
Delayed Expansion:
As Aacini explained the delayed expansion is safe against any special characters in the content (it can handle ALL characters from 0x01 to 0xFF).
恕我直言,有四种不同的扩展。
延迟扩展:
正如 Aacini 所解释的,延迟扩展对于内容中的任何特殊字符都是安全的(它可以处理从 0x01 到 0xFF 的所有字符)。
Percent Expansion:
The percent expansion can't handle or removes some characters (even with escaping).
It can be useful for simple content, as it can expand variables after an endlocal
barrier.
百分比扩展:
百分比扩展无法处理或删除某些字符(即使有转义)。
它对于简单的内容很有用,因为它可以在endlocal
屏障之后扩展变量。
setlocal
set "myVar=simple content"
(
endlocal
set result=%myVar%
)
FOR-Loop-Parameters expansion:
It is safe, if the delayed expansion is disabled, else the delayed expansion phase is executed after the expansion of the %%a variables.
It can be useful, as it can expand variables after an endlocal
barrier
FOR-Loop-Parameters 扩展:
如果禁用延迟扩展是安全的,否则延迟扩展阶段在 %%a 变量扩展后执行。
它很有用,因为它可以在endlocal
障碍之后扩展变量
setlocal EnableDelayedExpansion
set "var=complex content &<>!"
for /F "delims=" %%A in ("!var!") DO (
endlocal
set "result=%%A"
)
SET Expansion:set var
expands also a variable, and it is always safe and works independent of the delayed expansion mode.
SET 扩展:set var
扩展也是一个变量,它总是安全的并且独立于延迟扩展模式工作。
Aacini just explained how the call %%%var%%%
construct work, I only want to give some additional remarks.call
is stackable, you can use many of them and each restarts the parser.
Aacini 刚刚解释了call %%%var%%%
构造是如何工作的,我只想给出一些补充说明。call
是可堆叠的,您可以使用其中的许多,并且每个都会重新启动解析器。
set "var=%%var%%#"
call call call call call echo %var%
results to %var%######
结果 %var%######
But call
have many disadvantages/side effects!
Each call double all carets ^
You can say: "Hey I've tested it and I can't see any doubling"
但是call
有很多缺点/副作用!
每次调用都加倍所有插入符号^
你可以说:“嘿,我已经测试过了,我看不到任何加倍”
call call call call echo ^^
result ^
结果 ^
Nevertheless it's true, but it's mostly hidden, as each restart also have a special character phase where carets escapes the next character, but you can see the doubling effect with
尽管如此,这是真的,但它大多是隐藏的,因为每次重新启动也有一个特殊的字符阶段,其中插入符号会转义下一个字符,但是您可以看到加倍效果
call call call call echo "^^"^^
result "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"^
结果 "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"^
Even if a call expansion restarts the parser, you can never use the delayed expansion in any phase (only in the first one).
即使调用扩展重新启动解析器,您也永远不能在任何阶段(仅在第一个阶段)使用延迟扩展。
call
stops working if it detects unescaped special characters.
call
如果检测到未转义的特殊字符,则停止工作。
echo you ^& me
call echo you & me
call echo you ^& me
call echo you ^^& me
call echo you ^^^& me
Only the first results to the output you & me
, all the others fails.
只有第一个结果到 output you & me
,所有其他结果都失败。
Another problem is that call is extremly slow, a call set var=content
is ~50times slower than set var=content
, the cause is that call try to start an external program.
另一个问题是 call 非常慢,acall set var=content
比 慢~50 倍set var=content
,原因是 call 尝试启动外部程序。
@echo off
setlocal
(
echo echo *** External batch, parameters are '%%*'
) > set.bat
set "var="
call set var=hello
set var
I hope it was interesting a bit ...
And if you want to go more in depth you can read CALL me, or better avoid call
and How does the Windows Command Interpreter (CMD.EXE) parse scripts?
我希望它有点有趣......
如果你想更深入地阅读CALL me,或者更好地避免调用
和Windows 命令解释器 (CMD.EXE) 如何解析脚本?
回答by Aacini
This problem is not directly related to Delayed variable Expansion, but to the fact that two value expansions are required: the first one give the variable name and the second one must replace this name by its value. The direct way to do that is via two expansions in the same line as shown in the previous answer: set myvar=!%myvar%!
that works because %var% expansion is done before the command-line is analyzed for execution whereas !var! expansion is done later, just before the command is executed (hence the "delayed" name). This mean that %var% expansion may provide parts of the command and may cause syntax errors, but !var! not. For example if %var%==value ...
cause an error if var is empty or have spaces, but if !var!==value ...
never cause a syntax error.
这个问题与延迟变量扩展没有直接关系,而是与需要两次值扩展的事实有关:第一个给出变量名,第二个必须用它的值替换这个名称。直接的方法是通过同一行中的两个扩展,如上一个答案所示:set myvar=!%myvar%!
之所以有效,是因为 %var% 扩展是在分析命令行以供执行之前完成的,而 !var! 扩展是稍后完成的,就在命令执行之前(因此是“延迟”名称)。这意味着 %var% 扩展可能会提供命令的一部分并可能导致语法错误,但是 !var! 不是。例如if %var%==value ...
,如果 var 为空或有空格,则会导致错误,但if !var!==value ...
不会导致语法错误。
The double expansion of values may be achieved in other ways that does not involve Delayed variable Expansion. For example, we may create an auxiliary Batch file that do the second expansion:
值的双重扩展可以通过其他不涉及延迟变量扩展的方式来实现。例如,我们可以创建一个辅助 Batch 文件来进行第二次扩展:
echo myvar:%myvar%
echo set myvar=%%%myvar%%%> auxiliary.bat
call auxiliary
echo myvar:%myvar%
Previous method may be used to do a third or even deeper level expansions, and even be combined with Delayed Expansions to create very complex value managements. This matter is not just a curiosity, but the key to access array elements or linked lists. For example:
以前的方法可以用来做第三层甚至更深层次的扩展,甚至可以与延迟扩展相结合来创建非常复杂的价值管理。这件事不仅仅是好奇,而是访问数组元素或链表的关键。例如:
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
回答by shf301
What you're trying to do won't work - delayed expansion only changes the variable expansion behavior of a variable inside of a block. It doesn't allow you the aliasing/nesting (for a lack of a better word) that you are attempting.
您尝试做的事情是行不通的 - 延迟扩展只会改变块内变量的变量扩展行为。它不允许您尝试使用别名/嵌套(因为缺少更好的词)。
set myvar=first
sets the variable myvar to the text "first". set first=second
sets the variable first to the text "second. There is no link between those two lines. myvar
will never evaluate to something that it wasn't explicitly set to.
set myvar=first
将变量 myvar 设置为文本“first”。 set first=second
将变量 first 设置为文本“second。这两行之间没有链接。 myvar
永远不会评估为未明确设置的内容。
I don't believe there is anyway to accomplish what you are trying to do here.
我不相信无论如何都可以完成您在这里尝试做的事情。
* Edit *
* 编辑 *
OK after taking a look at your answerI seeing how that works, you can get your desired output with this:
好的,在查看您的答案后,我看到了它是如何工作的,您可以通过以下方式获得所需的输出:
@echo off
setlocal EnableDelayedExpansion
set myvar=first
set first=second
echo myvar:%myvar%
set myvar=!%myvar%!
echo myvar:%myvar%
So the magic seems to happen because of the way that standard and delayed expansion occur. The line set myvar=!%myvar%!
is seems be expanded first by the standard expander to set myvar=!first!
(you'll see this if you run the script with echo on
). Then the delayed expander runs and expands !first
to "second" and set's myvar
to that.
所以魔法似乎发生是因为标准和延迟扩展发生的方式。该行set myvar=!%myvar%!
似乎首先由标准扩展器扩展为set myvar=!first!
(如果使用 运行脚本,您将看到这一点echo on
)。然后延迟扩展器运行并扩展!first
到“第二个”并设置myvar
为那个。
I have no idea if this is documented behavior as to how standard and delayed expansion should work or just an implementation detail (which means it could break in the future)
我不知道这是否是关于标准和延迟扩展应该如何工作的记录行为,或者只是一个实现细节(这意味着它将来可能会中断)