Why does `%errorlevel%` expand to empty string in DOSBox?

1

Trying to automate some DOSBox scripting for an old program, I wanted to use the exit code usually in errorlevel to control the program flow.

The input is given in a hello_db.bat file:

@echo off
set msg=Hello
echo %msg%
dir
echo errorlevel = %errorlevel%

Then DOSBox is called with dosbox hello_db.bat.

The output is:

Hello
{Listing from dir}
errorlevel = 

So the %errorlevel% expanded to an empty string instead of the exit code from the dir command, where as the first msg variable could expand correctly.

How can I make DOSBox expand the errorlevel correctly in the script?

Short answer based on through answer by @phuclv:

The requirement was to insert a pause after the command if not OK (errorlevel != 0), so ended up doing:

set errorany=0
:: Command is inserted here...
if errorlevel 1 set errorany=1

:: Make pause if not OK
if %errorany%==1 pause

Morten Zilmer

Posted 2019-10-30T13:59:37.647

Reputation: 430

Answers

2

Because %ERRORLEVEL% is not a variable in MS-DOS. It's DR-DOS and cmd-specific. They're very different things

%ERRORLEVEL%

In COMMAND.COM of DR-DOS 7.02 and higher, this pseudo-variable returns the last error level returned by an external program or the RETURN command, f.e. "0".."255". See also the identically named pseudo-variable %ERRORLEVEL% under Windows and the IF ERRORLEVEL conditional command.

https://en.wikipedia.org/wiki/Environment_variable#DOS

Like %DATE% or many other variables, they're new features of cmd.exe and require command extension to be enabled

If Command Extensions are disabled, the following dynamic variables will be not accessible:

%CD% %DATE% %TIME% %RANDOM% %ERRORLEVEL% %CMDEXTVERSION% %CMDCMDLINE% %HIGHESTNUMANODENUMBER%

https://ss64.com/nt/syntax-variables.html

If you turn off command extension for DOS compatibility you'll lose access to those variables on Windows cmd.exe


If you have to stick with MS-DOS and its command.com then the only way to get errorlevel is to use if errorlevel in descending order

...
if errorlevel 4 set errorlvl=4
if errorlevel 3 set errorlvl=3
if errorlevel 2 set errorlvl=2
if errorlevel 1 set errorlvl=1

Fortunately if you really have to deal with all 255 error codes then you don't actually have to write 255 lines of code. But it's still rather long so see [§] below if you're interested

If you can install alternative shells (by setting SHELL=\path\to\shell.com in config.sys) then there are some 3rd party shells with an errorlevel variable. For example the famous 4DOS has %_?

Most internal 4DOS commands set errorlevel values. 4DOS has internal variables for external program exit code (%?), reason for external program termination (%??), exit code last internal command (%_?) and last DOS error (%_SYSERR). Since 4DOS is able to determine values equal, less than, less or equal, greater, greater or equal, unequal to another value, it's easy to catch specific errors:

IF "%_?" NE "" GOSUB ERR_HANDLER
:ERR_HANDLER
IF %_? == 1 ...
IF %_? == 2 ...
...
RETURN

The ON ERROR... command is also available to catch (and recover from) errors.


§ How to check for every possible errorlevel:

@ECHO OFF
REM Reset variables
FOR %%A IN (1 10 100) DO SET ERR%%A=

REM Check error level hundredfolds
FOR %%A IN (0 1 2) DO IF ERRORLEVEL %%A00 SET ERR100=%%A
IF %ERR100%==2 GOTO 200
IF %ERR100%==0 IF NOT "%1"=="/0" SET ERR100=

REM Check error level tenfolds
FOR %%A IN (0 1 2 3 4 5 6 7 8 9) DO IF ERRORLEVEL %ERR100%%%A0 SET ERR10=%%A
IF "%ERR100%"=="" IF %ERR10%==0 SET ERR10=

:1
REM Check error level units
FOR %%A IN (0 1 2 3 4 5) DO IF ERRORLEVEL %ERR100%%ERR10%%%A SET ERR1=%%A
REM Modification necessary for errorlevels 250+
IF NOT ERRORLEVEL 250 FOR %%A IN (6 7 8 9) DO IF ERRORLEVEL %ERR100%%ERR10%%%A SET ERR1=%%A
GOTO End

:200
REM In case of error levels over 200 both
REM tenfolds and units are limited to 5
REM since the highest DOS error level is 255
FOR %%A IN (0 1 2 3 4 5) DO IF ERRORLEVEL 2%%A0 SET ERR10=%%A
IF ERR10==5 FOR %%A IN (0 1 2 3 4 5) DO IF ERRORLEVEL 25%%A SET ERR1=%%A
IF NOT ERR10==5 GOTO 1

:End
REM Clean up the mess and show results
SET ERRORLEV=%ERR100%%ERR10%%ERR1%
FOR %%A IN (1 10 100) DO SET ERR%%A=
ECHO ERRORLEVEL  %ERRORLEV%

Credit: https://www.robvanderwoude.com/errorlevel.php

phuclv

Posted 2019-10-30T13:59:37.647

Reputation: 14 930

1Thanks a lot for the through answer; that is greatly appreciated :-) I have added a condensed solution to my question, to make it easier to apply for other that drop by. – Morten Zilmer – 2019-10-30T20:30:21.210

1

You should not attempt to use %ERRORLEVEL%, as that is more recent than DOS, as @phuclv states. Instead, use the older IF ERRORLEVEL n construct, which tests for whether the error level is n or greater. This means that you should test possible error levels in descending order:

IF ERRORLEVEL 5 ...  
IF ERRORLEVEL 4 ...  
IF ERRORLEVEL 3 ...
...

You should read the section on Detecting Errorlevel at Errorlevel at SS64, paying specific attention to the beginning where it discusses compatibility with DOS.

Jeff Zeitlin

Posted 2019-10-30T13:59:37.647

Reputation: 2 918