CMD中为何&&运算符不遵循%ErrorLevel%值,后续echo命令仍执行?
这问题我之前踩过坑,咱们一步步拆解为啥会出现这种不符合预期的情况~
问题复现
先把测试场景贴出来,方便大家看清楚矛盾点:
测试命令
cmd /c exit 99 echo %ErrorLevel% echo %ErrorLevel% && echo %ErrorLevel% echo %ErrorLevel%实际输出
99 99 99 99
按照常理,%ErrorLevel%是99(非0),&&后面的命令应该不会执行才对,但结果是它居然跑起来了,这就很让人摸不着头脑。
我原本的认知(和你完全一致)
我之前也笃定这两点:
&&的作用等价于if "%ErrorLevel%" equ "0",只有当前%ErrorLevel%为0时才执行后续命令echo命令绝对不会修改%ErrorLevel%的值
那为啥实际结果和认知不符呢?
我试过的排查步骤
我第一反应是不是延迟扩展的锅,于是开了延迟扩展测试——结果不管是在控制台直接跑,还是写在.bat/.cmd文件里,输出还是一模一样:
setlocal EnableDelayedExpansion cmd /c exit 99 echo %ErrorLevel% echo %ErrorLevel% && echo !ErrorLevel! echo %ErrorLevel%
问题的核心原因(终于搞懂了!)
咱们的核心误解出在对&&的判断逻辑上!&&根本不是判断全局的%ErrorLevel%变量,它只认紧挨着它前面的那个命令的退出码!
回到你出问题的那行命令:echo %ErrorLevel% && echo %ErrorLevel%
这里的&&判断的是前面echo %ErrorLevel%这个命令本身的退出码——而echo命令只要能正常输出内容,不管输出的是啥,它的退出码都是0!哪怕你输出的是99,echo本身执行成功了,退出码就是0,所以&&就认为“前一个命令没问题”,直接触发了后面的echo。
哦!原来如此!我们之前误以为&&盯着全局的%ErrorLevel%,但实际上它只关心自己前面那一步命令的执行结果。
验证&解决方法
要验证这个逻辑很简单,换个会失败的命令试试:
cmd /c exit 99 rem 故意执行一个不存在的文件的dir命令,让它返回非0退出码 dir non_existent_file.txt && echo 我不会被执行
这时候dir命令失败,退出码非0,&&后面的echo就不会跑,完全符合预期。
如果想让&&判断的是之前cmd /c exit 99的退出码,咱们可以把这个错误码转成前面命令的退出码:
cmd /c exit 99 echo %ErrorLevel% rem 用cmd /c exit %ErrorLevel%把当前ErrorLevel作为这个命令的退出码 cmd /c exit %ErrorLevel% && echo 我不会被执行 echo %ErrorLevel%
这时候cmd /c exit %ErrorLevel%的退出码是99,&&后面的内容就不会执行了,完美符合预期。
另外你之前试的延迟扩展为啥没用?因为问题根本不在变量扩展的时机,而是咱们对&&的判断逻辑理解错了,所以开不开延迟扩展都不影响结果。
备注:内容来源于stack exchange,提问作者acmctr

