哪些cmd.exe内部命令在成功执行后将ERRORLEVEL清零?

24
在Windows批处理脚本中处理错误的常见方法是使用诸如 if errorlevel 1 ...if %errorlevel% neq 0 ... 的内容。通常,人们希望错误处理代码保留 ERRORLEVEL。
我相信所有外部命令在执行后都会将 ERRORLEVEL 设置为某个值,因此在执行外部命令之前,错误处理代码必须将 ERRORLEVEL 保存到环境变量中。
但是内部命令怎么办?问题是,一些内部命令在成功时将 ERRORLEVEL 清除为 0,而有些则不会。我找不到任何文件说明哪些命令会清除 ERRORLEVEL。
那么问题来了:哪些内部命令会在成功时将 ERRORLEVEL 设置为 0?这与返回的 ERRORLEVEL 代码没有关系,严格只涉及成功结果。
有一些类似“最简单的将 ERRORLEVEL 重置为零的方法是什么?”“Windows 批处理文件:.bat vs .cmd?”的帖子给出了部分答案。但我从未见过综合性的列表。
注意: 多年来,我一直对此感到好奇。所以我最终决定进行一些实验,并得出明确的答案。我发布这个问答来分享我的发现。

2
请参见:https://dev59.com/vVsW5IYBdhLWcg3wbWxv - Aacini
2个回答

31

本答案基于我在Windows 10下运行的实验结果。我怀疑在使用cmd.exe的早期Windows版本中存在差异,但这是可能的。

还要注意-本答案没有尝试记录内部命令遇到错误时的ERRORLEVEL结果(除了DEL和ERASE的一点小说明)

不仅命令之间有所不同,而且单个命令在从命令行运行、批处理脚本(.bat扩展名)中运行或从批处理脚本(.cmd扩展名)中运行时可能会表现出不同的行为。

以下一组命令无论在任何情况下都不会将ERRORLEVEL清零,而是保留先前的ERRORLEVEL:

  • BREAK
  • CLS
  • ECHO
  • ENDLOCAL
  • FOR:显然DO子句中的命令可能会设置ERRORLEVEL,但一个成功的FOR至少有一个迭代,不会自己将ERRORLEVEL设置为0。
  • GOTO
  • IF:显然,由IF执行的命令可能会设置ERRORLEVEL,但成功的IF不会自己将ERRORLEVEL设置为0。
  • KEYS
  • PAUSE
  • POPD
  • RD
  • REM
  • RMDIR
  • SHIFT
  • START
  • TITLE

下一组命令不管在任何情况下都会在成功时将ERRORLEVEL清零:

  • CD
  • CHDIR
  • COLOR
  • COPY
  • DATE
  • DEL:即使DEL失败(当然如果没有文件参数运行),也总是清除ERRORLEVEL。
  • DIR
  • ERASE:即使ERASE失败,也总是清除ERRORLEVEL。 (当然如果没有文件参数运行)。
  • MD
  • MKDIR
  • MKLINK
  • MOVE
    • PUSHD:将当前目录推入一个栈中,并切换到指定目录。
    • REN:重命名或移动文件或目录。
    • RENAME:重命名或移动文件或目录。
    • SETLOCAL:启用局部化环境变量,使环境变量更易于管理。
    • TIME:显示或更改系统时间。
    • TYPE:显示文件内容。
    • VER:显示Windows版本号。
    • VERIFY:控制命令行的验证功能是否开启。
    • VOL:显示磁盘卷标和序列号。

    然后有一些命令不会在从命令行或使用扩展名为.bat的脚本中成功时清除ERRORLEVEL,但如果从扩展名为.cmd的脚本中发出,则会将ERRORLEVEL清除为0。请参见https://dev59.com/43VC5IYBdhLWcg3w51ny#148991https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J获取更多信息。

    • ASSOC:显示或修改文件关联。
    • DPATH:显示或更改执行程序的搜索路径。
    • FTYPE:显示或修改文件类型关联。
    • PATH:显示或更改执行程序的搜索路径。
    • PROMPT:显示或更改命令行提示符。
    • SET:显示、设置或删除环境变量。

    最后,有一些命令不适合归入前面的任何一类:

    • CALL:如果调用了一个:routine或批处理脚本,则ERRORLEVEL由被调用的脚本或:routine进行独占控制。但是,如果调用其他类型的成功命令,则如果被调用的命令未设置ERRORLEVEL,则始终将其清除为0。
      例如:call echo OK

    • EXIT:如果不使用/B选项,则cmd.exe会终止,并且没有更多的ERRORLEVEL,只有cmd.exe的返回代码。显然,EXIT /B 0会将ERRORLEVEL清除为0,但是不带值的EXIT /B会保留先前的ERRORLEVEL。

    我相信这涵盖了所有内部命令,除非我错过了某个未记录的命令。


只是一点小提示:在Windows NT 4.0上,rem会将ErrorLevel重置为零;据我所知,自从Windows XP以后,这个问题已经被修复了。 - aschipfl

4

你对 CALL 命令的描述不完整:

CALL:如果被调用的命令没有设置 ERRORLEVEL,则清除它。例如:call echo OK

请看这个简单的例子:

@echo off

call :setTwo
echo Set two: %errorlevel%

call :preserve
echo Preserve: %errorlevel%

call echo Reset
echo Reset: %errorlevel%

call :subNotExists 2> NUL
echo Sub not exist: %errorlevel%

goto :EOF

:setTwo
exit /B 2

:preserve
echo Preserve
exit /B

输出:

Set two: 2
Preserve
Preserve: 2
Reset
Reset: 0
Sub not exist: 1

CALL命令的描述应该像这样:

  • CALL:如果被调用的命令没有设置ERRORLEVEL,它将清除ERRORLEVEL。例如:call echo OK,但如果被调用的命令是一个子程序,它会保留之前的ERRORLEVEL。如果被调用的子程序不存在,它会将ERRORLEVEL设置为1。

2
我的答案并不与该答案相矛盾,因为问题只涉及成功的内部命令结果。您选择有选择性地添加关于未成功的命令结果的信息。这很有用,但并没有回答问题。我考虑过提出一个关于内部命令错误处理的一般性问题,但决定它太广泛了,而且回答困难。(尽管我希望有一天能看到一份全面的论文!)我还考虑有选择地在我的答案中包括错误结果,但选择保持清洁 - 严格关于成功结果。 - dbenham
1
请检查一下 - 我在描述DEL和ERASE时可能让一些错误的结果悄悄地溜进了我的答案中;-) - dbenham
除了“当子程序不存在时,ERRORLEVEL=1”之外,“如果被调用的命令是一个子程序,则它会保留先前的ERRORLEVEL”部分是指_成功_的结果。无论如何,我真的认为这个主题应该讨论一般情况下内部命令设置的值。如果您只简要描述这些条件,那么这部分不会太广泛,某些命令在“错误条件”下将ERRORLEVEL设置为1。在我看来,ERRORLEVEL =非零部分不能从ERRORLEVEL = 0部分的描述中排除。 - Aacini
2
你提到CALL子程序的观点是正确的。我思考了一下,意识到CALL和EXIT应该有它们自己的类别。关于其他方面,我们只能同意不同。 - dbenham

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