当我使用"&&"时,即使第一个命令失败了,为什么cmd会运行我的第二个命令?

8

我正在尝试学习Rust。我想编译一个程序,只有在成功编译后才运行它。因此,我尝试了以下操作:

rustc hello.rs && hello

但是hello.exe始终在运行,即使编译失败。
如果我尝试:
rustc hello.rs
echo Exit Code is %errorlevel% 

我收到了“退出代码为101”的错误信息。

据我理解,在cmd中,唯一的真值是0,而101显然不是真值,并且&&是惰性求值的,那么为什么会运行hello呢?


rustc.bat看起来像这样:

@echo off
SET DIR=%~dp0%
cmd /c "%DIR%..\lib\rust.0.11.20140519\bin\rustc.exe %*"
exit /b %ERRORLEVEL%

@Mark,rustc是一个可执行文件还是批处理文件?你可以运行 rustc.exe hello.rs 吗? - wimh
1
你能否修改RUSTC.BAT文件,在rustc.exe之后直接加入exit /b %errorlevel%?没有rustc.bat,这就像猜谜游戏,但这可能会导致&&按照最初的预期工作。 - Magoo
@Magoo - 那样做没有帮助。请参见我的答案。我对这种行为感到震惊! - dbenham
@Magoo 它确实以那个结束。请查看更新后的问题。 - mpen
@Magoo - 你没有理解我的解释重点 - 如果没有使用CALL,EXIT /B无法正常工作。我已经添加了测试脚本来演示这个问题。 - dbenham
显示剩余6条评论
2个回答

11

非常好奇。在前面放一个CALL,一切应该都没问题。

call rustc hello.rs && hello

我并不完全理解机制。我知道 &&|| 不直接读取动态的 %errorlevel% 值,而是在某个低层级上进行操作。它们根据最近执行的命令的结果有条件地触发,而不管当前的 %errorlevel% 值如何。|| 甚至可以在未设置 %errorlevel% 的失败情况下触发!请参见 Windows 中的文件重定向和 %errorlevel%batch: “rd” 的退出代码在错误时也为 0 的示例。

rustc 是批处理文件,其行为取决于是否使用了 CALL。如果没有使用 CALL,则 &&|| 运算符仅响应命令是否运行 - 它们忽略脚本的退出代码。使用 CALL,则它们正常响应脚本的退出代码,以及响应脚本无法运行(可能脚本不存在)的情况。

换句话说,批处理脚本只有在通过 CALL 启动时才会通知 &&|| 运算符有关退出代码的信息。

更新

经过仔细阅读 foxidrive 的(现已删除的)答案,我意识到情况更加复杂。

如果使用 CALL,则一切都能正常工作 - &&|| 会响应脚本返回的 ERRORLEVEL。脚本中的早期命令可能会将 ERRORLEVEL 设置为 1,只要没有后续脚本命令清除错误,返回的 ERRORLEVEL 1 将被正确报告给 &&||

如果未使用 CALL,则 &&|| 响应脚本中最后执行的命令的 errorcode。脚本中的早期命令可能会将 ERRORLEVEL 设置为 1。但是,如果最后一个命令是成功执行的 ECHO 语句,则 &&|| 会响应 ECHO 命令的成功而不是脚本返回的 ERRORLEVEL 1。

真正的问题在于,EXIT /B 1 除非通过 CALL 调用脚本,否则不会向 &&|| 报告 ERRORLEVEL。条件运算符检测到 EXIT 命令成功执行,并忽略返回的 ERRORLEVEL!

如果脚本执行的最后一个命令是:

cmd /c exit %errorlevel%

这将正确地将返回的ERRORLEVEL报告给&&||,无论脚本是否被CALL调用。

以下是一些演示我所说的测试脚本。

test1.bat

@echo off
:: This gives the correct result regardless if CALL is used or not
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)

test2.bat

@echo off
:: This only gives the correct result if CALL is used
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)
rem This command interferes with && or || seeing the returned errorlevel if no CALL

test3.bat

@echo off
:: This only gives the correct result if CALL is used
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)
rem Ending with EXIT /B does not help
exit /b %errorlevel%

test4.bat

@echo off
:: This gives the correct result regardless if CALL is used or not
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)
rem The command below solves the problem if it is the last command in script
cmd /c exit %errorlevel%

现在测试带有和不带有CALL的情况:

>cmd /v:on
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

>test1&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1

>test2&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Success, yet errorlevel=1

>test3&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Success, yet errorlevel=1

>test4&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1

>call test1&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1

>call test2&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1

>call test3&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1

>call test4&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1

>

编辑 - 重写了我的更新 - dbenham
这只蝙蝠实际上包含了 cmd /cexit /b; 请参见更新的问题。但是它可以与 CALL 一起使用。 - mpen
2
@Mark - 如果脚本没有被调用,那么EXIT /B命令将掩盖返回代码。它只在脚本执行的最后一个命令是cmd /c exit %errorlevel%时才起作用。最简单的做法可能是始终在调用脚本时使用CALL。 - dbenham

1
快速演示以证明观点:

@ECHO OFF
SETLOCAL
CALL q24983584s 0&&ECHO "part one"
ECHO done one
CALL q24983584s 101&&ECHO "part two"
ECHO done two

GOTO :EOF

其中 q24983584s.bat 的位置

@ECHO OFF
SETLOCAL
EXIT /b %1

GOTO :EOF

按预期工作...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接