Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Read current errorlevel value inside a vbscript 3

Status
Not open for further replies.

ESquared

Programmer
Dec 23, 2003
6,129
US
Here's what I'm trying to do:

MyProgram.exe | cscript myvbscript.vbs /message:"reporting errorlevel %errorlevel%"

The problem with this is that the expanded %errorlevel% value is the one from before MyProgram.Exe runs, not after. When I tried /message:"errorlevel $errorlevel$" and in the script replacing $'s with %'s and running that through Shell.ExpandEnvironmentStrings it didn't work. The string "%errorlevel%" isn't expanded, probably because it's not a "real" environment string but replaced specially by the command interpreter.

I thought I could do something like Shell.Exec("echo %errorlevel%") and then read out the error level with StdOut.ReadLine on the exec object that returns, but Exec is looking for an executable file name and doesn't accept instructions for the command interpreter. ("The system cannot find the file specified.")

I know that I can Exec a process from inside the vbscript and get the errorlevel back from it:

cscript myvbscript.vbs /commandline:"MyProgram.exe" /message:"reporting errorlevel %errorlevel%"

but that's not how I would like to use this .vbs script.

Searching for vbscript and errorlevel yields 17,000 hits, mostly talking about setting errorlevel on exit or other random things with batch files & errorlevels, but not what I am looking for. Any ideas?

[COLOR=#aa88aa black]Cum catapultae proscriptae erunt tum soli proscript catapultas habebunt.[/color]
 
Okay, I found

CMD /C "EXIT %errorlevel%"

but I don't like it. Isn't there a better way to learn the errorlevel?

[COLOR=#aa88aa black]Cum catapultae proscriptae erunt tum soli proscript catapultas habebunt.[/color]
 
And that doesn't work anyway...

According to Batch Scripts for Windows, cscript can get access to ERRORLEVEL:

There are two separate executable files to read and execute scripts:

cscript.exe for output to command window (MS DOS prompt) and stdout
wscript.exe for interactive interface with graphical dialog boxes
Only cscript.exe can use the ERRORLEVEL built-in environment variable that contains the exit code value from the last command run using the command processor (cmd.exe).
But I am still having trouble finding how to do this, because ExpandEnvironmentStrings does not work. Any ideas?

[COLOR=#aa88aa black]Cum catapultae proscriptae erunt tum soli proscript catapultas habebunt.[/color]
 
I think what the quoted part you posted means is that cscript.exe can return an exit code to cmd.exe which you can then access using %errorlevel% and not necessarily saying that cscript.exe can easily read %errorlevel%.

What if you tried separating the values you pass into your script to: "message" and "errorlevel" so that you can call it with something like.

MyProgram.exe | cscript myvbscript.vbs /message:"reporting errorlevel" /errorlevel:%errorlevel%

--------------------------------------------------------------------------------
dm4ever
My philosophy: K.I.S.S - Keep It Simple Stupid
 
>MyProgram.exe | cscript myvbscript.vbs /message:"reporting errorlevel %errorlevel%"
Even though it does not work, it conveys the intention that the commandline be executed at prompt. If that's the case, put this config into a .bat file and it would work. Could you check?
[tt]
[blue]::batch file : MyProgram.bat
@echo off
MyProgram.exe
::the following line are for checking only, comment out after
echo %errorlevel%
cscript.exe //nologo myvbscript.vbs /message:"reporting errorlevel %errorlevel%"[/blue]
[/tt]
 
dm4ever,

Whether inside or outside of quotes, %errorlevel% contains the value of errorlevel before the executable runs.

tsuji,

I will try what you've suggested at work on Monday.

In another forum I got the suggestion to use:

Code:
setlocal enabledelayedexpansion
MyProgram.exe | cscript myvbscript.vbs /message:"reporting errorlevel !errorlevel!"
But this, too, returns an errorlevel of 0 even though I know MyProgram.exe is returning a higher errorlevel. So the execution of cscript or myvbscript.vbs are themselves setting errorlevel to 0?

It looks like I am going to have to execute the program from within the vbscript, so I can see its return code AND get its standard output. The reason I don't like this is that with the former method, the program runs no matter what is wrong with my script or even if my script file is missing, or say the script interpreter. With the second method, I have to rely on the script to run. So perhaps I'll use the class_terminate trick to set errorlevel in case of an error and in my batch file will check, and if it is set, will run the program by itself.

(See... I'm modifying an existing batch file that I can't replace with pure vbscript or another solution, and we want logging to the application event log, depending on what the program does, thank god it writes to standard out and sets errorlevel. MOM will then hit the event log to notify us of a problem.)

[COLOR=black #d0d0d0]When I walk, I sometimes bump into things. I am closing my eyes so that the room will be empty.[/color]
 
What are you trying to do here?

Is there a reason you are trying to run the program and then the script in a piped I/O sequence? Clearly Cmd is going to have to do string expansion and then parse the whole command line before processing any of it. There is no other way for it to work.

Just as clearly the program will not have been run at the time the expansion is done. Using [tt]setlocal[/tt] isn't going to help you here. It can only alter expansion behavior even further. The way you are using it sets ERRORLEVEL to 0 as well, on purpose.

The setlocal command sets the ERRORLEVEL variable. If you pass either {enableextensions | disableextensions} or {enabledelayedexpansion | disabledelayedexpansion}, the ERRORLEVEL variable is set to zero (0). Otherwise, it is set to one (1).

The comment about access to the ERRORLEVEL environment variable in a CScript is talking about using [tt]WshShell.ExpandEnvironmentStrings()[/tt] method calls within a script.


The easiest fix is to run the program on one command line and the script on another. If the script is really and truly processing the StdOut stream from the program as its StdIn, you'll have to redirect the program output to a file and then run the scripting redirecting its StdIn from the saved file.
 
dilettante,

Is there a reason you are trying to run the program and then the script in a piped I/O sequence?
Yep.

Clearly Cmd is going to have to do string expansion
Clearly!

and then parse the whole command line before processing any of it. There is no other way for it to work.
Really?

Create the following as a batch file and run it, then tell me if you want to change your statement:
Code:
set e=6
setlocal enabledelayedexpansion
for %%a in (5 4 3 2 1) do set e=!e!%%a&echo !e!
I'm sorry to waste your time, but I thought that what I'm trying to do should be obvious. I want both the errorlevel of a program AND its standard output (I'll let you imagine why, but that's not relevant). There is theoretically no reason why a program should not be able to examine the errorlevel of the previous item in a chain of piped output, although obviously handing this information in via command line parameters isn't working so far.

If the script is really and truly processing the StdOut stream from the program as its StdIn
Yes, it's really and truly processing the StdOut stream from the program as its StdIn. Do you want to see my script? It works great.

you'll have to redirect the program output to a file and then run the scripting redirecting its StdIn from the saved file
I thought of this option before I even began this, but I do not want to do it this way, although I will if I have to. I think it's poor practice to use temporary files when there are ways to avoid them.

The comment about access to the ERRORLEVEL environment variable in a CScript is talking about using WshShell.ExpandEnvironmentStrings() method calls within a script.
Have you really and truly tried this? If you'll read the OP, I said "running that through Shell.ExpandEnvironmentStrings ... didn't work." All other environment strings are expanded just fine, but "%errorlevel%" returns... drum roll please... "%errorlevel%".

I do appreciate you taking the time to try to help me. If it seems like I responded a little strangely, that's because I feel a bit defensive because of the attitude of your post. It's interesting how people react when criticized, especially if the criticism implies superior knowledge that actually turns out to be inferior knowledge.

[COLOR=black #d0d0d0]When I walk, I sometimes bump into things. I am closing my eyes so that the room will be empty.[/color]
 
I never meant to imply that [tt]WshShell.ExpandEnvironmentStrings()[/tt] would work, I was just clarifying what that quote posted above was talking about.

Since ERRORLEVEL isn't a "real" environment variable I'm not sure how it could work. The dynamic variables aren't recorded in the Cmd.exe process's environment block, which is what gets passed to CScript.exe when it runs.

Your FOR example is using delayed expansion. This doesn't apply to single commands.

Example:

level.vbs
Code:
WScript.Quit CInt(WScript.Arguments(0))
leveltest.cmd
Code:
prompt $G
setlocal enabledelayedexpansion
cscript //nologo level.vbs -1
cscript //nologo level.vbs 13 & echo !errorlevel!
cscript //nologo level.vbs -1
cscript //nologo level.vbs 13 | echo !errorlevel!
pause
Results:

[tt]C:\Documents and Settings\Raymond\Desktop\test>prompt $G
>setlocal enabledelayedexpansion
>cscript //nologo level.vbs -1
>cscript //nologo level.vbs 13 & echo !errorlevel!
13
>cscript //nologo level.vbs -1
>cscript //nologo level.vbs 13 | echo !errorlevel!
-1
>pause
Press any key to continue . . .[/tt]

Using "&" we have two commands, and it is possible to have delayed expansion operate here. But using "|" we have a single command from Cmd.exe's "point of view" so delayed expansion is irrelevant.
 
Maybe the reason for this behavior isn't clear.

When you pipe output from one program to another's input in Cmd.exe it doesn't use an intermediate temp file. Thus it actually has to run both at the same time.

Example:

pauser.vbs
Code:
WScript.StdErr.WriteLine WScript.Arguments(0)
WScript.Sleep 5000
WScript.StdErr.WriteLine WScript.Arguments(0)
pausertest.cmd
Code:
prompt $G
cscript //nologo pauser.vbs first | cscript //nologo pauser.vbs second
pause
Results:

[tt]C:\Documents and Settings\Raymond\Desktop\errorlevel>prompt $G
>cscript //nologo pauser.vbs first | cscript //nologo pauser.vbs second
first
second
first
second
>pause
Press any key to continue . . .[/tt]
 
dilettante,

Thank you for the explanation. That it has to run both at once was not clear but I see why it is true. That also explains (finally) why the errorlevel of the previous program is not available in the next one (and probably never will be).

In regards to the quote from the above site... why would they say "Only cscript.exe can use the ERRORLEVEL built-in environment variable that contains the exit code value from the last command run using the command processor (cmd.exe)" if it doesn't work? And why would you say

The comment about access to the ERRORLEVEL environment variable in a CScript is talking about using WshShell.ExpandEnvironmentStrings() method calls within a script.
if it doesn't work?

Anyway... you've at least solved the mystery and kept me from doing any more search to try to complete this task the way I had imagined it. Thank you.

[COLOR=black #d0d0d0]When I walk, I sometimes bump into things. I am closing my eyes so that the room will be empty.[/color]
 
>...That also explains (finally) why the errorlevel of the previous program is not available in the next one (and probably never will be).

Now I am not overly convinced. Maybe I have not read the nice detail of dilettante's posts.

Let me give you a simple simulation:
[ul][li]first trigger an errorlevel by a program (a simple vbscript)[/li],[li] then pick up in another "program" this time a vbs (as requested) through named argument.[/li][/ul]

[1] The vbs generating an errorlevel, say, 99. (It is playing the role of MyProgram.exe.)
[tt]
'generate_errlev.vbs
wscript.echo "will generate errorlevel 99"
wscript.sleep 3000 'side-test of validity of synchroneity
wscript.quit 99
[/tt]
[2] The vbs that demonstrates picking up of the errorlevel 99.
[tt]
'pickup_errlev.vbs
if wscript.arguments.named.exists("errlev") then
msgbox wscript.arguments.named("errlev") 'this will pop out to avoid interference on the console
end if
[/tt]
[3] The scheme I proposed in my previous post will do the demonstration.
[tt]
::demo.bat - execute it at command prompt
cscript.exe //nologo generate_errlev.vbs
@echo off
cscript.exe //nologo pickup_errlev.vbs /errlev:"report error %errorlevel%"
echo end
[/tt]
I can therefore demonstrate the scheme work out as expected.

 
tsuji, try it by piping the output of one into the other... I will when I can, too.

[COLOR=black #d0d0d0]When I walk, I sometimes bump into things. I am closing my eyes so that the room will be empty.[/color]
 
tsuji the key issue is when running two programs or commands piped, as ESquared said. When joned into a single command that way Cmd.exe must evaluate the whole thing once because only then can it run the two in parallel.

As far as the other business about [tt]ExpandEnvironmentStrings()[/tt] goes, I was just saying that the quote from Batch Scripts for Windows didn't imply that your original line of BAT/CMD code would work. I thought that was what you were assuming.

That guy seems to have led all of us astray. I wonder what ever led him to say it? When I looked closer into things it became clear that ERRORLEVEL is not available to a CScript internally because it isn't actually an environment variable at all!

If Command Extensions are enabled, then there are several dynamic environment variables that can be expanded but which don't show up in the list of variables displayed by SET. These variable values are computed dynamically each time the value of the variable is expanded. If the user explicitly defines a variable with one of these names, then that definition will override the dynamic one described below:

%CD% - expands to the current directory string.

%DATE% - expands to current date using same format as DATE command.

%TIME% - expands to current time using same format as TIME command.

%RANDOM% - expands to a random decimal number between 0 and 32767.

%ERRORLEVEL% - expands to the current ERRORLEVEL value

%CMDEXTVERSION% - expands to the current Command Processor Extensions version number.

%CMDCMDLINE% - expands to the original command line that invoked the Command Processor
These are just calculated values built on the fly by Cmd.exe and don't exist in the environment block of the process.
 
me said:
The string "%errorlevel%" isn't expanded, probably because it's not a "real" environment string but replaced specially by the command interpreter.

[COLOR=black #d0d0d0]When I walk, I sometimes bump into things. I am closing my eyes so that the room will be empty.[/color]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top