using %errorlevel% or %something% in a compound batch command ()

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
MicrosoftIsKillingMe
Posts: 55
Joined: 11 Dec 2017 09:08

using %errorlevel% or %something% in a compound batch command ()

#1 Post by MicrosoftIsKillingMe » 18 Dec 2017 14:23

I'm going to present the question as a command line issuance, with one ampersand, rather than a .BAT file, for your convenience.
If you don't have c:\autoexec.bat, adjust the following to be a file that does exist.

dir c:\autoexec.bat & echo errorlevel is %errorlevel%

3 questions on the above compound command in XP. FWIW, ver says
Microsoft Windows Xp [Version 5.1.2600]
And if anyone cares, I'm running cmd.exe and ntvdm.exe from a .LNK of
%SystemRoot%\system32\cmd.exe /kc:\mydos.bat

1. When I issue the compound command back to back, the first time it echos the errorlevel PRIOR to the command.
Each ensuing time, it reports the errorlevel as 0.

To see this, now do the same thing with a nonexistent filename, twice back to back
dir c:\autoexec.XXX & echo errorlevel is %errorlevel%
dir c:\autoexec.XXX & echo errorlevel is %errorlevel%
For me it shows 0 the first time, and 1 every time afterwards.

If you then go back to the original one
dir c:\autoexec.bat & echo errorlevel is %errorlevel%
dir c:\autoexec.bat & echo errorlevel is %errorlevel%
it shows 1 the first time, and 0 every time afterwards.

I do not have an environment variable named errorlevel, so that's not an issue.

First, do you reproduce it, or is it just me?

I assume that it is because the interpretation ("expansion" ?) of %errorlevel% is done immediately rather than after the DIR. (BTW I'm inexperienced with the concept of delayed expansion but is that a factor here, or is that concept only pertinent to normal environment variables?). Right?

-----------
2. Is %errorlevel% indeed the dos errorlevel of "if errorlevel 1" fame? (where, as noted earlier, SET shows nothing for ERRORLEVEL)

3. If so, then why do some people still say to do the age old reverse order dance of
if errorlevel 4 ...
if errorlevel 3 ...
if errorlevel 2 ...
if errorlevel 1 ...

or why do something like "if errorlevel 3 if not errorlevel 4"

since you can test %errorlevel% directly? Is it that the direct accessing capability only became available in a later version of DOS, such as mine, and they are merely answering in such a way that would work in every ver back to DOS 1 or something?
(Did the %errorlevel% representation of the internal variable ERRORLEVEL only come into being in recent versions?)

TIA

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: using %errorlevel% or %something% in a compound batch command ()

#2 Post by Squashman » 18 Dec 2017 17:21

Seems like you pretty much answer your own questions.

Variables are always expanded before the commands execute. So before you even issued the DIR command, the errorlevel variable had already expanded to the last known errorlevel.
Easy enough to check.

Code: Select all

C:\Users\Squashman>set errorlevel=5

C:\Users\Squashman>dir /b notfound.txt &echo errrolevel %errorlevel%
File Not Found
errrolevel 5
In regards to reverse errorlevels, the CHOICE command uses that syntax.

MicrosoftIsKillingMe
Posts: 55
Joined: 11 Dec 2017 09:08

Re: using %errorlevel% or %something% in a compound batch command ()

#3 Post by MicrosoftIsKillingMe » 18 Dec 2017 18:20

Thanks Squashman. So no way to get errorlevel until a subsequent line of the .BAT runs it sounds like. And that %errorlevel% is the errorlevel set by processes(question 2)? And that the item 3 things that some people advise are a waste of time, and DOS version is not a factor in that? I didn't hear a resounding confirmation of those by your answer but it sounds like you're concurring with those statements.

What you did with set errorlevel=5 I believe goes afield. That command sets a DOS environment variable named errorlevel, not the DOS errorlevel. E.g.
1 set errorlevel=5
2. Go DIR foo!_97.xyz (some nonexistent file)
3. DOS errorlevel becomes 1
4. Environment variable ERRORLEVEL is neither created nor modified.
5. Go DIR c:\autoexec.bat (some existing file)
6. DOS errorlevel becomes 0
7. Environment variable ERRORLEVEL is neither created nor modified.

So as I understand this, that SET statement isn't of any practical use.

Perhaps confusion comes from the following, which is only my understanding and you experts who know it can assert this: These are two items, however they are commonly referenced by %errorlevel%. If the environment variable exists, it is expanded for %errorlevel% and DOS errorlevel is ignored. If it doesn't exist, you get the DOS errorlevel, I gather (that's basically my original question 2). As to whether they are completely distinct entities or have other commonality, I don't know.

To someone who has a strong understanding of the distinction - here's your chance to shine :)

MicrosoftIsKillingMe
Posts: 55
Joined: 11 Dec 2017 09:08

Re: using %errorlevel% or %something% in a compound batch command ()

#4 Post by MicrosoftIsKillingMe » 18 Dec 2017 19:27

Well it turns out that I at least have an answer to the question 1 part. A very simple to understand page covered this
https://ss64.com/nt/delayedexpansion.html

So if I just do as it directs
- setlocal enabledelayedexpansion
- use bangs (!) instead of percent signs

and voila, the desired sequential evaluation occurs. That is,
setlocal enabledelayedexpansion
dir c:\autoexec.bat&echo errorlevel is !errorlevel!

will display the value of errorlevel AFTER the first part of the compound command. The site states "For simple commands this will make no noticable difference, but with loop commands like FOR, compound or bracketed expressions delayed expansion will allow you to always see the current value of the variable." Since I'm compound here, it's just what I was looking for.

Whee! Now that I think I have a bit of logical understanding I hope to take much better advantage of @dbenham and his extensive useful posting about delayed expansion here and stackexchange.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: using %errorlevel% or %something% in a compound batch command ()

#5 Post by Squashman » 18 Dec 2017 19:45

You asked for a command line way to do it so I gave you a command line answer. You now switched your code to a batch file.

If you really want to do it on the command line all in one line then you can do this.

Code: Select all

C:\Users\Squashman>cmd /V:ON /C "dir /b notfound.txt &echo !errorlevel!"
File Not Found
1

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: using %errorlevel% or %something% in a compound batch command ()

#6 Post by dbenham » 18 Dec 2017 22:34

Another command line option:

Code: Select all

dir /b notfound.txt & call echo %^errorlevel%
CALL introduces a 2nd round of expansion, and unlike batch, expansion of an undefined variable preserves the code rather expanding to nothing. The caret is considered part of the name, so it is not found, and then the caret is consumed, leaving %errorlevel% for the 2nd round of expansion.

If you are in a batch file, then the CALL solution is:

Code: Select all

dir /b notfound.txt & call echo %%errorlevel%%
Delayed expansion is typically preferred over the CALL hack, but there are times when delayed expansion is not wanted.


Dave Benham

jfl
Posts: 226
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: using %errorlevel% or %something% in a compound batch command ()

#7 Post by jfl » 19 Dec 2017 06:22

Funny coincidence, I was about to ask the same question, but in a slightly different context... And none of the above solutions seem to work there:
I'm trying to filter the output of a command in a make file, to select the output lines I'm really interested in.
The problem is the same when trying to tee the output into a log file, so I'll use that as an example:

Code: Select all

compile mysource | tee logfile
The problem in that case is that the errorlevel of the pipe is that of the filter at the right of the pipe, not that of the initial command at the left.
And in that case, make (actually nmake) will never see failures of the initial compilation command. (Or, if the filter is findstr, wrongly report failures if the filter finds nothing worth outputing).

I hoped to work around that by storing the errorlevel code into a temporary file, then returning it in the end. Ex:

Code: Select all

((false & echo !ERRORLEVEL! >el.tmp) | tee NUL) & for /f %e in (el.tmp) do @del el.tmp & echo el.tmp contains %e
But I can't make it to work: I tried %ERRORLEVEL%, %%ERRORLEVEL%%, !ERRORLEVEL!, call %^ERRORLEVEL%, etc, with various numbers of %, !, and ^.
No matter what I do, it outputs the previous ERRORLEVEL from _before_ the whole line was executed (usually 0), possibly surrounded by some % characters. Or sometines I just get the string ERRORLEVEL!
Ex:

Code: Select all

#3 E:on V:on C:\JFL\Temp>false | tee NUL

#3 E:on V:on C:\JFL\Temp>echo %ERRORLEVEL%
0

#3 E:on V:on C:\JFL\Temp>false & echo !ERRORLEVEL!
1

#3 E:on V:on C:\JFL\Temp>(false & echo !ERRORLEVEL!) | tee NUL
!ERRORLEVEL!

#3 E:on V:on C:\JFL\Temp>
Can anybody find a way to get a single line of script _with_a_pipe_ that exits with the errorlevel of the first command in that pipe?

PS. Unrelated: The good (but slow) way to set the errorlevel to 5 is not to set a variable, but to run the command:

Code: Select all

cmd /c exit 5

MicrosoftIsKillingMe
Posts: 55
Joined: 11 Dec 2017 09:08

Re: using %errorlevel% or %something% in a compound batch command ()

#8 Post by MicrosoftIsKillingMe » 19 Dec 2017 06:39

Thanks for that nifty tidbit Dave! Makes perfect sense. BTW my end goal in fact is a .BAT (who likes to type when you can just do everything with foo.bat :) ); I was just trying to save responders a whopping 8 or 10 keystrokes by phrasing the question as such so they would not having to create, run, and delete a file :) Thank you for making the important point that adding the layer of .BAT execution affects expansion, though some form of delayed expansion solves both command line and .BAT evaluation (EDIT: by CALL or another cmd /C for the former, and setlocal(etc.) for the latter). (So, since I'm using a .BAT, one might wonder why I had a compound command anyway, since you can freely separate commands. Sometimes I just do, to shorten the visual vertical size; but more practically, sometimes I use parentheses blocks which are effectively compound statements.)

I still have numerous uncertain speculations in my posts above so if someone is knowledgeable about the fine distinctions between a created environment variable ERRORLEVEL and the internal DOS variable ERRORLEVEL I'll remain tuned in to this station :mrgreen: I'm particularly puzzled that so many experts' forum posts instruct to do the "dance" I referred to in question 3 of the O.P., if in fact it's really unnecessary. (or maybe unnecessary with XP DOS, vs. earlier DOS?)

MicrosoftIsKillingMe
Posts: 55
Joined: 11 Dec 2017 09:08

Re: using %errorlevel% or %something% in a compound batch command ()

#9 Post by MicrosoftIsKillingMe » 19 Dec 2017 08:17

I think that my remaining questions are cleared up in
https://ss64.com/nt/errorlevel.html
In short, SET ERRORLEVEL creates a distinct entity than DOS errorlevel; if the environment version exists, it takes precedence for expansion; jfl's handy "exit trick" is agreed with; and just use EQU (and NEQ, etc.) with %errorlevel% instead of the "dance". There are many other nifty tidbits there about errorlevel, and even quotes the great Steve McQueen 8) .

Further good support is at
https://blogs.msdn.microsoft.com/oldnew ... 0/?p=20743
One takeaway is the suggestion to consider unsetting ERRORLEVEL atop a .BAT if one wants to see the errorlevel inside %errorlevel%.

https://stackoverflow.com/questions/334 ... mmand-line
gives a warning about errorlevel that completely eluded me until I read this: "Testing ErrorLevel works for console applications, but (...) this won't work if you're trying to run a windowed application (e.g. Win32-based) from a command prompt." (Brrr! But solution is to use start /wait foo.exe)

And (drum roll) https://stackoverflow.com/questions/681 ... rror-level
posits that "the %ERRORLEVEL% variable support was added in WinNT."
(EDIT: that means, generally, Windows NT; Windows 2000 - MIGHT need to be Pro, untested; Windows XP; Vista; and Win 7, 8, 10. Basically %errorlevel% as the internal DOS variable is for XP and later. Lucky W.A. guess by me earlier.)

I'm very embarrassed that my preliminary Googling was so inept that I didn't catch these excellent resources in the first place :oops:

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: using %errorlevel% or %something% in a compound batch command ()

#10 Post by aGerman » 19 Dec 2017 11:15

%errorlevel% is a dynamic variable (as %date%, %time%, %random%, %cd%, ...). Those are NO environment variables and thus, they don't show up using SET. As soon as an environment variable with the same name exists (e.g. if you used SET to give it a different value) the value of the environment variable gets precedence over the dynamic variable. Changes of the dynamic variable will not change the environment varibale with the same name.
Please execute SET /? for more informations.
The help text of (cmd.exe-)internal commands begins always with the old behavior as it was known from command.com. As soon as you read "If Command Extensions are enabled ..." the paragraphs of the new bahavior (introduced with NT) begin. Usually extensions are enabled by default (registry setting) but you can en-/disable it manually. See SETLOCAL /?.

I think everything was already said about the early expansion of variables if they are changed in the same command line or an parenthesized block of command lines.

As to IF ERRORLEVEL 1 versus IF %ERRORLEVEL%==1 :
The first will still work even if an environment variable errorlevel was set. It will also work in the same command line because there is no variable that has to be expanded.
But the both statements are not the same because IF ERRORLEVEL 1 does not mean "If the errorlevel value equals one, then ...". The actual meaning is "If the errorlevel value equals or is greater than one, then ...". Thus, the first IF statement would be also evaluate to TRUE if the %errorlevel% variable expands to 2 or maybe 47398.
For more information execute IF /?.

Steffen

MicrosoftIsKillingMe
Posts: 55
Joined: 11 Dec 2017 09:08

Re: using %errorlevel% or %something% in a compound batch command ()

#11 Post by MicrosoftIsKillingMe » 19 Dec 2017 11:30

@ jfl: for the pipe problem, maybe try messing with the FOR /F technique as illustrated in
https://stackoverflow.com/questions/488 ... soft-nmake

I have a feeling that
https://stackoverflow.com/questions/111 ... ed-command
might benefit you as it compares alternatives. According to him it seems you were on track in using a "capture" file. Then his best-liked approach (with doskeys) actually came from dostips.
And it's dbenham's post anyway so it's reliable :D :D

Sorry, I got lost with what your "false" does and not following how you're using make and nmake, so this is somewhat a wild shot. BTW thanks for (accurately) chipping in on setting errorlevel.
Last edited by MicrosoftIsKillingMe on 19 Dec 2017 13:22, edited 3 times in total.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: using %errorlevel% or %something% in a compound batch command ()

#12 Post by aGerman » 19 Dec 2017 11:48

@jfl

Both sides of a pipe are executed in separate cmd.exe processes like that:
C:\WINDOWS\System32\cmd.exe /S /D /c"YourCommandHere"
I think that answers your questions :wink:

Steffen

Post Reply