为什么在运行时将空格插入此批处理文件?

8bi*_*ree 9 cmd batch-file

我有这个简单的测试脚本:

echo start
falsey ^
&& echo nope
echo end
Run Code Online (Sandbox Code Playgroud)

falsey是一个微小的C程序,我帮助测试,它只是返回1:

int main(int argc, const char** argv) {
        return 1;
}
Run Code Online (Sandbox Code Playgroud)

问题是,当我打开cmd并运行这个批处理脚本时,它会在两个&s 之间插入几个空格,这自然会导致它错误地回显nope:

C:\jenkins-foo-test>foo.bat
C:\jenkins-foo-test>echo start
start
C:\jenkins-foo-test>falsey &  & echo nope
nope
C:\jenkins-foo-test>echo end
end
Run Code Online (Sandbox Code Playgroud)

我已经找到了一些解决方法,例如在以下位置引用引号&&:

falsey ^
"&&" echo nope
Run Code Online (Sandbox Code Playgroud)

在这种情况下,行情实际上并不起作用,请参阅@ dbenham的答案.

放弃^并合并两条线:

falsey && echo nope
Run Code Online (Sandbox Code Playgroud)

或者在前面添加一个空格&&:

falsey ^
 && echo nope
Run Code Online (Sandbox Code Playgroud)

这三个选项中的每一个都无法正确打印nope.但是当cmd.exe打印出正在运行的命令时,它们仍然会有额外的空格.

那么为什么要插入这些额外的空间?如何阻止cmd.exe破坏事物而不必一直将我的代码扭曲成温和不自然的形状?

dbe*_*ham 8

行继续^转义下一行的第一个字符,因此第&一个字符被视为文字并作为参数传递给falsey.第二个&被视为简单的命令连接.

&&被视为条件命令连接的一个简单解决方案是&&在之前放置:

falsey &&^
echo nope
Run Code Online (Sandbox Code Playgroud)

或者你可以在前面放一个空格 &&

falsey ^
 && echo nope
Run Code Online (Sandbox Code Playgroud)

另一个选择是使用jeb在他的链接答案中指出的重定向技巧,但我从不使用该技巧.


关于引用的"解决方案" "&&"- 你在欺骗自己.它似乎在falsey失败时起作用.但如果你的命令成功,那你就有麻烦:

echo ONE ^
"&&" echo TWO
Run Code Online (Sandbox Code Playgroud)

- 输出 -

C:\test>echo ONE "  && " echo TWO
ONE "
'" echo TWO' is not recognized as an internal or external command,
operable program or batch file.
Run Code Online (Sandbox Code Playgroud)

请注意,&&因为第一个"是有效的,所以&&没有引用.如果在之前添加空格"&&"

echo ONE^
 "&&" echo TWO
Run Code Online (Sandbox Code Playgroud)

然后你得到以下

C:\test>echo ONE "&&" echo TWO
ONE "&&" echo TWO
Run Code Online (Sandbox Code Playgroud)

因为现在空间被转义并被"&&"引用.


关于您的注释,您可以忽略这些额外的空间 - 它们是解析器在ECHO为ON时如何显示行的工件.解析器通常会重新排列行,但通常不会影响结果.

例如

   <    nul  set test=OK
echo [%test%]
Run Code Online (Sandbox Code Playgroud)

--OUTPUT--

C:\test>set test=OK 0<nul

C:\test>echo [OK]
[OK]
Run Code Online (Sandbox Code Playgroud)

注意回声SET线是如何完全重新排列的.重定向被压缩并移动到语句的末尾,在重定向之前有一个空格.您可能认为该空间将包含在作业中,但您可以看到它不是:-)

您可能不得不担心重新排列的唯一一次是在管道中使用该命令.

例如:

(set test=OK&call echo [%%^^test%%])|findstr "^"
Run Code Online (Sandbox Code Playgroud)

--OUTPUT--

C:\test>(set test=OK  & call echo [%^test%] )  | findstr "^"
[OK ]
Run Code Online (Sandbox Code Playgroud)

您可以看到SET值中包含一个不需要的额外空间.这是如何实现管道的工件 - 每个端都在新的CMD.EXE进程中执行,并且该行被多次解析.您可以通过使用%CMDCMDLINE%显示传递给左侧cmd.exe的命令行来查看空间来源.

(set test=OK&call echo [%%^^test%%] %%^^cmdcmdline%%)|findstr "^"
Run Code Online (Sandbox Code Playgroud)

--OUTPUT--

C:\test>(set test=OK  & call echo [%^test%] %^cmdcmdline% )  | findstr "^"
[OK ] C:\WINDOWS\system32\cmd.exe  /S /D /c" ( set test=OK & call echo [%^test%] %^cmdcmdline% )"
Run Code Online (Sandbox Code Playgroud)

请参阅为什么在管道代码块内部延迟扩展失败?有关许多带管道怪癖的更多信息.特别是,要注意所选答案,以便对正在发生的事情做出正确解释.