Wie erhalte ich den Anwendungs-Exit-Code über eine Windows-Befehlszeile?

797

Ich führe ein Programm aus und möchte sehen, wie sein Rückkehrcode lautet (da es aufgrund unterschiedlicher Fehler unterschiedliche Codes zurückgibt).

Ich weiß, dass ich dies in Bash durch Laufen tun kann

echo $?

Was mache ich, wenn ich cmd.exe unter Windows verwende?

Skrud
quelle
8
Auch bei SuperUser gefragt: Wie überprüfe
Deanna
1
Googelte nach "Win8 Wie bekomme ich eine CMD-Eingabeaufforderung, um den Exit-Status anzuzeigen", wie wir es unter Linux tun können. Dies war die beste Auswahl und ist genau.
SDsolar
1
Sie können schnell sehen, welche App zurückkehrt:app.exe & echo %errorlevel%
marbel82

Antworten:

976

Eine Pseudo-Umgebungsvariable mit dem Namen errorlevelspeichert den Exit-Code:

echo Exit Code is %errorlevel%

Außerdem hat der ifBefehl eine spezielle Syntax:

if errorlevel

Siehe if /?für Details.

Beispiel

@echo off
my_nify_exe.exe
if errorlevel 1 (
   echo Failure Reason Given is %errorlevel%
   exit /b %errorlevel%
)

Warnung: Wenn Sie einen Umgebungsvariablennamen festlegen errorlevel, %errorlevel%wird dieser Wert und nicht der Exit-Code zurückgegeben. Verwenden Sie ( set errorlevel=), um die Umgebungsvariable zu löschen und den Zugriff auf den wahren Wert von errorlevelüber die %errorlevel%Umgebungsvariable zu ermöglichen.

DrFloyd5
quelle
38
Wenn Sie direkt über eine Windows-Befehlszeile ausgeführt werden und immer 0 zurückgegeben wird, lesen
Ken
9
Auch wenn Sie in Powershell sind, können Sie verwendenecho Exit Code is $LastExitCode
Brandon Pugh
11
Hinweis: "Fehlerstufe 1" ist wahr, wenn Fehlerstufe> = 1. "Fehlerstufe 0" stimmt also mit allem überein. Sehen ob /?". Stattdessen können Sie "if% ERRORLEVEL% EQU 0 (..)" verwenden.
Curtis Yallop
1
Es wurden Fälle gefunden, in denen %ERRORLEVEL%0 ist, obwohl ein Fehler aufgetreten ist. Passiert beim Einchecken %ERRORLEVEL%einer Cmd-Datei. Der Versuch start /waithat nicht funktioniert. Das einzige, was funktioniert hat, istif errorlevel 1 (...)
AlikElzin-kilaka
1
Freundlicher Rat:% ErrorLevel% ist eine Shell-Variable, keine Umgebungsvariable, und es wird auch eine stringNicht- Variable zurückgegeben int, was bedeutet, dass Sie EQ/ NEQeffektiv nicht verwenden können.
KayleeFrye_onDeck
277

Das Testen ErrorLevelfunktioniert für Konsolenanwendungen , aber wie von dmihailescu angedeutet, funktioniert dies nicht, wenn Sie versuchen, eine Fensteranwendung (z. B. Win32-basiert) über eine Eingabeaufforderung auszuführen . Eine gefensterten Anwendung wird im Hintergrund ausgeführt werden , und die Steuerung kehrt sofort an die Eingabeaufforderung (höchstwahrscheinlich mit einem ErrorLevelvon Null , um anzuzeigen , dass der Prozess wurde erstellt erfolgreich). Wenn eine Fensteranwendung schließlich beendet wird, geht ihr Beendigungsstatus verloren.

Anstatt den an anderer Stelle erwähnten konsolenbasierten C ++ - Starter zu verwenden, besteht eine einfachere Alternative darin, eine Fensteranwendung mit dem Befehl der Eingabeaufforderung zu starten START /WAIT. Dadurch wird die Fensteranwendung gestartet, auf das Beenden gewartet und die Steuerung an die Eingabeaufforderung zurückgegeben, wobei der Beendigungsstatus des Prozesses festgelegt ist ErrorLevel.

start /wait something.exe
echo %errorlevel%
Gary
quelle
20
Vielen Dank für die Idee "START / wait". Das hat bei mir funktioniert :)
Timotei
3
schöner Fang. Ich wusste nichts über diesen Befehl. Ich habe gerade gesehen, wie es für> start / wait notepad.exe
dmihailescu
1
Ein weiterer Grund, warum es möglicherweise nicht funktioniert (immer Null), ist, wenn es sich in einem ifoder befindet for. Verwenden Sie !errorlevel!stattdessen, wie in dieser Antwort beschrieben .
Roman
23

Wenn Sie den Fehlercode genau abgleichen möchten (z. B. gleich 0), verwenden Sie Folgendes:

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

if errorlevel 0Übereinstimmungen errorlevel> = 0. Siehe if /?.

Curtis Yallop
quelle
Ist es Groß- und Kleinschreibung zu beachten?
Nishant
1
Vars, Befehle (einschließlich "if") und "equ" funktionieren unabhängig vom Fall.
Curtis Yallop
14

Es funktioniert möglicherweise nicht richtig, wenn Sie ein Programm verwenden, das nicht an die Konsole angeschlossen ist, da diese App möglicherweise noch ausgeführt wird, während Sie glauben, den Exit-Code zu haben. Eine Lösung dafür in C ++ sieht wie folgt aus:

#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;
}
dmihailescu
quelle
In einigen Konfigurationen sollten Sie #include <atlstr.h> hinzufügen, damit der CString-Typ erneut erkannt wird.
Jake OPJ
8

Es ist erwähnenswert, dass .BAT- und .CMD-Dateien unterschiedlich funktionieren.

Beim Lesen von https://ss64.com/nt/errorlevel.html wird Folgendes festgestellt:

Es gibt einen wesentlichen Unterschied zwischen der Art und Weise, wie .CMD- und .BAT-Batchdateien Fehlerstufen festlegen:

Ein altes .BAT-Batch-Skript, das die 'neuen' internen Befehle ausführt: APPEND, ASSOC, PATH, PROMPT, FTYPE und SET, setzt ERRORLEVEL nur, wenn ein Fehler auftritt. Wenn Sie also zwei Befehle im Batch-Skript haben und der erste fehlschlägt, bleibt ERRORLEVEL auch nach erfolgreichem zweiten Befehl gesetzt.

Dies kann das Debuggen eines problematischen BAT-Skripts erschweren. Ein CMD-Batch-Skript ist konsistenter und setzt ERRORLEVEL nach jedem Befehl, den Sie ausführen [source].

Dies verursachte mir kein Ende der Trauer, als ich aufeinanderfolgende Befehle ausführte, aber der ERRORLEVEL würde auch im Falle eines Fehlers unverändert bleiben.

RockDoctor
quelle
0

Irgendwann musste ich Protokollereignisse von Cygwin genau in das Windows-Ereignisprotokoll übertragen. Ich wollte, dass die Nachrichten in WEVL benutzerdefiniert sind, den richtigen Exit-Code, Details, Prioritäten, Nachrichten usw. haben. Deshalb habe ich ein kleines Bash-Skript erstellt, um dies zu erledigen. Hier ist es auf GitHub, logit.sh .

Einige Auszüge:

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"

Hier ist der temporäre Dateiinhaltsteil:

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"

Hier ist eine Funktion zum Erstellen von Ereignissen 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
}

Ausführen des Batch-Skripts und Aufrufen von __create_event:

cmd /c "$(cygpath -wa "$LGT_TEMP_FILE")"
__create_event
jonretting
quelle