969

I am running a program and want to see what its return code is (since it returns different codes based on different errors).

I know in Bash I can do this by running

echo $?

What do I do when using cmd.exe on Windows?

Skrud
  • 11,604
  • 5
  • 24
  • 22
  • 8
    Also asked on SuperUser: [How to check the exit code of the last command in batch file?](http://superuser.com/q/194662/126252) – Deanna Jun 24 '13 at 11:42
  • 1
    Googled for "Win8 How to get CMD prompt to show exit status" like we can do in Linux. This was top selection, and is accurate. – SDsolar May 21 '18 at 20:33
  • 1
    You can quickly see what app returns: `app.exe & echo %errorlevel%` – marbel82 Feb 19 '19 at 10:28

7 Answers7

1168

The "exit code" is stored in a shell variable named errorlevel.

The errorlevel is set at the end of a console application. Windows applications behave a little differently; see @gary's answer below.

Use the if command keyword errorlevel for comparison:

if errorlevel <n> (<statements>)

Which will execute statements when the errorlevel is greater than or equal to n. Execute if /? for details.

A shell variable named errorlevel contains the value as a string and can be dereferenced by wrapping with %'s.

Example script:

my_nifty_exe.exe

rem Give resolution instructions for known exit codes.
rem Ignore exit code 1.
rem Otherwise give a generic error message.

if %errorlevel%==7 (
   echo "Replace magnetic tape."
) else if %errorlevel%==3 (
   echo "Extinguish the printer."
) else if errorlevel 2 (
   echo Unknown Error: %errorlevel% refer to Run Book documentation.
) else (
   echo "Success!"
)

Warning: An environment variable named errorlevel, if it exists, will override the shell variable named errorlevel. if errorlevel tests are not affected.

DrFloyd5
  • 13,555
  • 3
  • 25
  • 34
  • 48
    If you're running directly from a Windows command line and always seeing 0 returned, see Gary's answer: http://stackoverflow.com/a/11476681/31629 – Ken Aug 30 '12 at 13:51
  • 17
    Also if you're in powershell you can use `echo Exit Code is $LastExitCode` – Brandon Pugh Jan 16 '14 at 19:47
  • 15
    Note: "errorlevel 1" is true if errorlevel >= 1. So "errorlevel 0" will match everything. See "if /?". Instead, you can use "if %ERRORLEVEL% EQU 0 (..)". – Curtis Yallop Jul 29 '14 at 16:06
  • 2
    Found cases where `%ERRORLEVEL%` is 0 even though an error occurred. Happened when checking `%ERRORLEVEL%` in a cmd file. Trying `start /wait` didn't work. The only thing that worked is `if errorlevel 1 (...)` – AlikElzin-kilaka Apr 13 '15 at 12:57
  • 4
    Friendly advice: %ErrorLevel% is a shell variable, not an environment variable, and it also returns a `string` not an `int`, meaning you can't use `EQ`/`NEQ` effectively. – kayleeFrye_onDeck Sep 24 '16 at 00:27
  • I wonder if this can be put into the prompt like I have in bash? – SDsolar May 26 '18 at 02:21
331

Testing ErrorLevel works for console applications, but as hinted at by dmihailescu, this won't work if you're trying to run a windowed application (e.g. Win32-based) from a command prompt. A windowed application will run in the background, and control will return immediately to the command prompt (most likely with an ErrorLevel of zero to indicate that the process was created successfully). When a windowed application eventually exits, its exit status is lost.

Instead of using the console-based C++ launcher mentioned elsewhere, though, a simpler alternative is to start a windowed application using the command prompt's START /WAIT command. This will start the windowed application, wait for it to exit, and then return control to the command prompt with the exit status of the process set in ErrorLevel.

start /wait something.exe
echo %errorlevel%
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Gary
  • 4,426
  • 1
  • 22
  • 19
  • 6
    nice catch. I did not know about that command. I've just seen it working for > start /wait notepad.exe – dmihailescu Jan 23 '13 at 18:48
  • 3
    Another reason why it might not work (always zero) is when it's inside an `if` or `for`. Consider using `!errorlevel!` instead, as [described in this answer](http://stackoverflow.com/a/4368104/33080). – Roman Starkov Apr 08 '15 at 22:36
142

Use the built-in ERRORLEVEL Variable:

echo %ERRORLEVEL%

But beware if an application has defined an environment variable named ERRORLEVEL!

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 6
    It's not an actual environment variable (which is, obviously, why it ceases to work if there *is* a variable named that way). – Joey Jun 26 '10 at 08:13
  • 31
    @SteelBrain: It's called `$LastExitCode` in PowerShell. – Alex A. Mar 09 '15 at 15:21
39

If you want to match the error code exactly (eg equals 0), use this:

@echo off
my_nify_exe.exe
if %ERRORLEVEL% EQU 0 (
   echo Success
) else (
   echo Failure Reason Given is %errorlevel%
   exit /b %errorlevel%
)

Note that if errorlevel 0 matches errorlevel >= 0.
See if /?.

Or, if you don't handle success:

if  %ERRORLEVEL% NEQ 0 (
    echo Failed with exit-code: %errorlevel%
    exit /b %errorlevel%
)
Top-Master
  • 7,611
  • 5
  • 39
  • 71
Curtis Yallop
  • 6,696
  • 3
  • 46
  • 36
18

It's worth noting that .BAT and .CMD files operate differently.

Reading https://ss64.com/nt/errorlevel.html it notes the following:

There is a key difference between the way .CMD and .BAT batch files set errorlevels:

An old .BAT batch script running the 'new' internal commands: APPEND, ASSOC, PATH, PROMPT, FTYPE and SET will only set ERRORLEVEL if an error occurs. So if you have two commands in the batch script and the first fails, the ERRORLEVEL will remain set even after the second command succeeds.

This can make debugging a problem BAT script more difficult, a CMD batch script is more consistent and will set ERRORLEVEL after every command that you run [source].

This was causing me no end of grief as I was executing successive commands, but the ERRORLEVEL would remain unchanged even in the event of a failure.

RockDoctor
  • 331
  • 3
  • 11
14

It might not work correctly when using a program that is not attached to the console, because that app might still be running while you think you have the exit code. A solution to do it in C++ looks like below:

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
#include "tchar.h"
#include "stdio.h"
#include "shellapi.h"

int _tmain( int argc, TCHAR *argv[] )
{

    CString cmdline(GetCommandLineW());
    cmdline.TrimLeft('\"');
    CString self(argv[0]);
    self.Trim('\"');
    CString args = cmdline.Mid(self.GetLength()+1);
    args.TrimLeft(_T("\" "));
    printf("Arguments passed: '%ws'\n",args);
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    if( argc < 2 )
    {
        printf("Usage: %s arg1,arg2....\n", argv[0]);
        return -1;
    }

    CString strCmd(args);
    // Start the child process. 
    if( !CreateProcess( NULL,   // No module name (use command line)
        (LPTSTR)(strCmd.GetString()),        // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        0,              // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
    ) 
    {
        printf( "CreateProcess failed (%d)\n", GetLastError() );
        return GetLastError();
    }
    else
        printf( "Waiting for \"%ws\" to exit.....\n", strCmd );

    // Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );
    int result = -1;
    if(!GetExitCodeProcess(pi.hProcess,(LPDWORD)&result))
    { 
        printf("GetExitCodeProcess() failed (%d)\n", GetLastError() );
    }
    else
        printf("The exit code for '%ws' is %d\n",(LPTSTR)(strCmd.GetString()), result );
    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
    return result;
}
svick
  • 236,525
  • 50
  • 385
  • 514
dmihailescu
  • 1,625
  • 17
  • 15
0

At one point I needed to accurately push log events from Cygwin to the Windows Event log. I wanted the messages in WEVL to be custom, have the correct exit code, details, priorities, message, etc. So I created a little Bash script to take care of this. Here it is on GitHub, logit.sh.

Some excerpts:

usage: logit.sh [-h] [-p] [-i=n] [-s] <description>
example: logit.sh -p error -i 501 -s myscript.sh "failed to run the mount command"

Here is the temporary file contents part:

LGT_TEMP_FILE="$(mktemp --suffix .cmd)"
cat<<EOF>$LGT_TEMP_FILE
    @echo off
    set LGT_EXITCODE="$LGT_ID"
    exit /b %LGT_ID%
EOF
unix2dos "$LGT_TEMP_FILE"

Here is a function to to create events in WEVL:

__create_event () {
    local cmd="eventcreate /ID $LGT_ID /L Application /SO $LGT_SOURCE /T $LGT_PRIORITY /D "
    if [[ "$1" == *';'* ]]; then
        local IFS=';'
        for i in "$1"; do
            $cmd "$i" &>/dev/null
        done
    else
        $cmd "$LGT_DESC" &>/dev/null
    fi
}

Executing the batch script and calling on __create_event:

cmd /c "$(cygpath -wa "$LGT_TEMP_FILE")"
__create_event
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jonretting
  • 412
  • 2
  • 6