Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Asked Answered
M

2

24

A frequent method to handling errors within Windows batch scripts is to use things like
if errorlevel 1 ... or if %errorlevel% neq 0 .... Often times one wants the error handling code to preserve the ERRORLEVEL.

I believe all external commands will always result in ERRORLEVEL being set to some value, so the error handling code must preserve the ERRORLEVEL in an environment variable prior to executing an external command.

But what about internal commands? The problem is, some internal commands clear the ERRORLEVEL to 0 when they succeed, and some do not. And I can't find any documentation specifying which commands do what.

So the question is, which internal commands clear the ERRORLEVEL to 0 upon success? This is not a general question about returned ERRORLEVEL codes, but strictly about success results.

There are posts like What is the easiest way to reset ERRORLEVEL to zero? and Windows batch files: .bat vs .cmd? that give partial answers. But I have never seen a comprehensive list.

Note: I've been curious about this for years. So I finally decided to run a bunch of experiments and come up with a definitive answer. I'm posting this Q&A to share what I have found.

Monastery answered 23/1, 2016 at 19:46 Comment(1)
See: #34988385Thallophyte
M
31

This answer is based on experiments I ran under Windows 10. I doubt there are differences with earlier Windows versions that use cmd.exe, but it is possible.

Also note - This answer does not attempt to document the ERRORLEVEL result when an internal command encounters an error (except for a wee bit concerning DEL and ERASE)

Not only are there difference between commands, but a single command can behave differently depending on whether it was run from the command line, or within a batch script with a .bat extension, or from within a batch script with a .cmd extension.

The following set of commands never clear the ERRORLEVEL to 0 upon success, regardless of context, but instead preserve the prior ERRORLEVEL:

  • BREAK
  • CLS
  • ECHO
  • ENDLOCAL
  • FOR : Obviously, commands in the DO clause may set the ERRORLEVEL, but a successful FOR with at least one iteration does not set the ERRORLEVEL to 0 on its own.
  • GOTO
  • IF : Obviously, commands executed by IF may set the ERRORLEVEL, but a successful IF does not set ERRORLEVEL to 0 on its own.
  • KEYS
  • PAUSE
  • POPD
  • RD
  • REM
  • RMDIR
  • SHIFT
  • START
  • TITLE

The next set of commands always clear the ERRORLEVEL to 0 upon success, regardless of context:

  • CD
  • CHDIR
  • COLOR
  • COPY
  • DATE
  • DEL : Always clears ERRORLEVEL, even if the DEL fails (except when run without any file argument).
  • DIR
  • ERASE : Always clears ERRORLEVEL, even if ERASE fails. (except when run without any file argument).
  • MD
  • MKDIR
  • MKLINK
  • MOVE
  • PUSHD
  • REN
  • RENAME
  • SETLOCAL
  • TIME
  • TYPE
  • VER
  • VERIFY
  • VOL

Then there are these commands that do not clear ERRORLEVEL upon success if issued from the command line or within a script with a .bat extension, but do clear the ERRORLEVEL to 0 if issued from a script with a .cmd extension. See https://mcmap.net/q/16007/-windows-batch-files-bat-vs-cmd and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J for more info.

  • ASSOC
  • DPATH
  • FTYPE
  • PATH
  • PROMPT
  • SET

Lastly, there are these commands that do not fit neatly into any of the prior categories:

  • CALL : If a :routine or batch script is CALLed, then ERRORLEVEL is exclusively controlled by the CALLed script or :routine. But any other type of successful CALL to a command will always clear ERRORLEVEL to 0 if the CALLed command does not otherwise set it.
    Example: call echo OK.

  • EXIT : If used without /B, then the cmd.exe session terminates and there is no more ERRORLEVEL, just the cmd.exe return code. Obviously EXIT /B 0 clears the ERRORLEVEL to 0, but EXIT /B without a value preserves the prior ERRORLEVEL.

I believe that accounts for all internal commands, unless there is an undocumented command that I missed.

Monastery answered 23/1, 2016 at 19:47 Comment(1)
...just a side note: rem reset the ErrorLevel to zero on Windows NT 4.0; as far as I can remember, this has been fixed since Windows XP...Uvarovite
T
4

Your description of CALL command is incomplete:

CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it. Example: call echo OK.

Check this small example:

@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

Output:

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

CALL description should say something like this:

  • CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it. Example: call echo OK, but if the called command is a subroutine it preserves the prior ERRORLEVEL. If the called subroutine does not exist, it sets the ERRORLEVEL to 1.
Thallophyte answered 24/1, 2016 at 11:43 Comment(4)
My answer does not contradict this answer, given that the question is only about successful internal command results. You chose to selectively add information about an unsuccessful command result. It is useful info, but does not answer the question. I thought about asking a general question about internal command error handling, but decided it was too broad, and difficult to answer. (Though I would like to see a comprehensive treatise some day!) I also thought about selectively including error results in my answer, but opted to keep it clean - strictly about success results.Monastery
Check that - I did let a little bit of error results sneak into my answer with my description of DEL and ERASE ;-)Monastery
Well, excluding the "ERRORLEVEL=1 when subroutine does not exist", the "if the called command is a subroutine it preserves the prior ERRORLEVEL" part refers to a successful result. Anyway, I really think that this topic should talk about values set by internal commands in general. I think this part would not be too broad if you just mention that certain commands set the ERRORLEVEL to 1 "in error conditions" with just a very brief description of such conditions. IMO the ERRORLEVEL = not zero part can not be excluded from a description of the ERRORLEVEL = 0 part.Thallophyte
Your point about CALLed subroutines is valid. I've done some more thinking, and realized CALL and EXIT deserve their own category. Regarding the rest, we will just have to agree to disagree.Monastery

© 2022 - 2024 — McMap. All rights reserved.