Discussion forum for all Windows batch related topics.
Moderator: DosItHelp
-
Liviu
- Expert
- Posts: 470
- Joined: 13 Jan 2012 21:24
#1
Post
by Liviu » 18 Feb 2014 02:15
The set/? help says "
SET command will set the ERRORLEVEL to 1 if the variable name is not found in the current environment". However, it says nothing about how/whether SET sets the ERRORLEVEL otherwise. Empirical evidence seems to indicate that a "successful" SET sets the ERRORLEVEL to 0 in a batch file, while it leaves the ERRORLEVEL unchanged when executed at the cmd prompt - which is a rather curious inconsistency.
The following code saved as a batch file
(edit:) with extension CMD (end edit)Code: Select all
@echo off
setlocal
setlocal enableExtensions disableDelayedExpansion
echo(
echo setlocal enableExtensions disableDelayedExpansion
call :test
endlocal
setlocal enableExtensions enableDelayedExpansion
echo(
echo setlocal enableExtensions enableDelayedExpansion
call :test
endlocal
setlocal disableExtensions
echo(
echo setlocal disableExtensions
(call;)
if errorlevel 1 (echo bat 1 - 0 cli) else (echo bat 0 - 0 cli)
(call)
if not errorlevel 1 (echo bat 0 - 1 cli) else (echo bat 1 - 1 cli)
set dummy=ignore
if not errorlevel 1 (echo bat 0 - 1 cli) else (echo bat 1 - 1 cli)
endlocal
echo(
echo cmd /v /c
cmd /v /c "(call;) & echo cli !errorlevel! - 0 cli & (call) & echo cli !errorlevel! - 1 cli & set "dummy=ignore" & echo cli !errorlevel! - 1 cli & exit /b"
exit /b
:test
(call;)
echo bat %errorlevel% - 0 cli
(call)
echo bat %errorlevel% - 1 cli
set dummy=ignore
echo bat %errorlevel% - 1 cli
goto :eof
returns (as tested under xp and win7)
Code: Select all
C:\tmp>set-ret
setlocal enableExtensions disableDelayedExpansion
bat 0 - 0 cli
bat 1 - 1 cli
bat 0 - 1 cli
setlocal enableExtensions enableDelayedExpansion
bat 0 - 0 cli
bat 1 - 1 cli
bat 0 - 1 cli
setlocal disableExtensions
bat 0 - 0 cli
bat 1 - 1 cli
bat 0 - 1 cli
cmd /v /c
cli 0 - 0 cli
cli 1 - 1 cli
cli 1 - 1 cli
where the curious part is the "bat 0 - 1 cli" lines showing that a non-0 errorlevel was reset to 0 after a SET in the batch file, but "cli 1 - 1 cli" left unchanged when executed directly (which can be verified by actually typing the commands at the cmd prompt).
Liviu
Last edited by
Liviu on 18 Feb 2014 11:18, edited 1 time in total.
-
dbenham
- Expert
- Posts: 2461
- Joined: 12 Feb 2011 21:02
- Location: United States (east coast)
#2
Post
by dbenham » 18 Feb 2014 07:02
That test script seems a bit wigged out to me - too much going on for me to figure out the point of everything. It especially seems odd that the 3rd round of batch tests has cmd extensions disabled, but the 4th round with command prompt tests using cmd /c has extensions enabled.
But anyway, I cannot reproduce your results unless I give the batch script a .CMD extension instead of .BAT. That is a known behavior of CMD.EXE: Most internal commands do not set ERRORLEVEL to 0 upon success if the script extension is .BAT. One exception is (CALL;). But internal commands do set the ERRORLEVEL to 0 upon success if the script extension is .CMD.
The issue with SET that I find curious/irritating is that SET "VAR=" raises an error on XP, but does not on Vista and beyond.
Dave Benham
-
Liviu
- Expert
- Posts: 470
- Joined: 13 Jan 2012 21:24
#3
Post
by Liviu » 18 Feb 2014 11:34
dbenham wrote:That test script seems a bit wigged out to me - too much going on for me to figure out the point of everything.
Main point is that "set dummy=ignore" sets the errorlevel to 0 when run in a batch file with extension CMD, while it leaves the errorlevel unchanged when run at the cmd prompt, and this happens regardless of the extension/expansion modes. It's a curious inconsistency, and one that I was not aware of.
dbenham wrote:But anyway, I cannot reproduce your results unless I give the batch script a .CMD extension instead of .BAT. That is a known behavior of CMD.EXE: Most internal commands do not set ERRORLEVEL to 0 upon success if the script extension is .BAT. One exception is (CALL;). But internal commands do set the ERRORLEVEL to 0 upon success if the script extension is .CMD.
Right, thanks for pointing. I edited the original post to make that clear.
FWIW the BAT vs CMD behavior difference was semi-officially "documented" in an old thread on the long gone MS newsgroups
https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/qEiVugDAcCUJ.
Mark Zbikowski (MSFT) wrote:The differences between .CMD and .BAT as far as CMD.EXE is concerned are:
With extensions enabled, PATH/APPEND/PROMPT/SET/ASSOC in .CMD files will set ERRORLEVEL regardless of error. .BAT sets ERRORLEVEL only on errors.
(...)
Well... I peeked at the sources. The differences stem from a desire in OS/2 to "fix" COMMAND.COM; to wit, that every call (with extensions enabled) would change the errorlevel in a manner so you could detect if a command failed or not. Now... without spending a ton of time with the sources, I don't know if this REALLY addressed that issue or not.
Liviu
-
dbenham
- Expert
- Posts: 2461
- Joined: 12 Feb 2011 21:02
- Location: United States (east coast)
#4
Post
by dbenham » 19 Feb 2014 22:47
Liviu wrote:Main point is that "set dummy=ignore" sets the errorlevel to 0 when run in a batch file with extension CMD, while it leaves the errorlevel unchanged when run at the cmd prompt, and this happens regardless of the extension/expansion modes. It's a curious inconsistency, and one that I was not aware of.
It has nothing to do with the SET command specifically. All the internal commands will behave that way when in batch mode with a batch script extension of CMD.
One point I find interesting is that it is the outermost running batch script that determines the behavior. If a .BAT script calls a .CMD script, then all of the internal commands in .CMD will behave as if the extension were .BAT, so they will not set the ERRORLEVEL upon success.
Likewise, if a .CMD script calls a .BAT script, then all the internal commands in the .BAT script will behave as if the extension were .CMD, so they will clear the ERRORLEVEL to 0 upon success.
Dave Benham
-
Liviu
- Expert
- Posts: 470
- Joined: 13 Jan 2012 21:24
#5
Post
by Liviu » 19 Feb 2014 23:17
dbenham wrote:It has nothing to do with the SET command specifically. All the internal commands will behave that way when in batch mode with a batch script extension of CMD.
Sorry, but I think there still is a basic misunderstanding. The inconsistency in point here is between the cmd prompt and batch execution - not between BAT vs CMD batch extensions. This inconsistency certainly does not exist for "all internal commands". Do a 'dir C:::\' and errorlevel will be set to 1, then do a 'dir C:\' and the errorlevel gets reset to 0. This happens whether you issue the 'dir' commands in a batch file or at the cmd prompt. However, the 'set' command resets the errorlevel to 0 in one case but not the other. That is the inconsistency I was talking about. You may find it obvious or irrelevant, however I hadn't seen it spelled out before and found it rather curious.
Liviu
-
dbenham
- Expert
- Posts: 2461
- Joined: 12 Feb 2011 21:02
- Location: United States (east coast)
#6
Post
by dbenham » 20 Feb 2014 10:05
It is more of a mixed bag than I realized.
The following internal commands clear the ERRORLEVEL to 0 upon success:
CD
CALL command
COLOR
COPY
DATE
DEL
DIR
MD
MOVE
PUSHD
REN
SETLOCAL
TIME
TYPE
VER
VERIFY
VOL
The following internal commands do not set the ERRORLEVEL upon success unless run in batch mode with .CMD extension:
ASSOC
CALL :label
CLS
ECHO
ENDLOCAL
FOR
FTYPE
GOTO
IF
PATH
PAUSE
POPD
PROMPT
RD
REM
SET
SHIFT
START
TITLE
Dave Benham