Jak uzyskać kod wyjścia aplikacji z wiersza polecenia systemu Windows?

797

Korzystam z programu i chcę zobaczyć, jaki jest jego kod powrotu (ponieważ zwraca różne kody na podstawie różnych błędów).

Wiem, że w Bash mogę to zrobić, uruchamiając

echo $?

Co mam zrobić, używając cmd.exe w systemie Windows?

Skrud
źródło
1
Googled na „Win8 Jak uzyskać CMD, aby pokazać status wyjścia”, tak jak możemy to zrobić w Linuksie. To był najlepszy wybór i jest dokładny.
SDsolar
1
Możesz szybko zobaczyć, która aplikacja zwraca:app.exe & echo %errorlevel%
marbel82

Odpowiedzi:

976

Pseudo zmienna środowiskowa o nazwie errorlevelprzechowuje kod wyjścia:

echo Exit Code is %errorlevel%

Ponadto ifpolecenie ma specjalną składnię:

if errorlevel

Zobacz if /?szczegóły.

Przykład

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

Ostrzeżenie: jeśli ustawisz nazwę zmiennej środowiskowej errorlevel, %errorlevel%zwróci tę wartość, a nie kod wyjścia. Użyj ( set errorlevel=), aby wyczyścić zmienną środowiskową, umożliwiając dostęp do prawdziwej wartości za errorlevelpośrednictwem %errorlevel%zmiennej środowiskowej.

DrFloyd5
źródło
38
Jeśli korzystasz bezpośrednio z wiersza poleceń systemu Windows i zawsze widzisz 0, zobacz odpowiedź Gary'ego: stackoverflow.com/a/11476681/31629
Ken
9
Również jeśli jesteś w PowerShell, możesz użyćecho Exit Code is $LastExitCode
Brandon Pugh
11
Uwaga: „poziom błędu 1” jest prawdziwy, jeśli poziom błędu> = 1. Zatem „poziom błędu 0” będzie pasował do wszystkiego. Zobacz „if /?”. Zamiast tego możesz użyć „jeśli% ERRORLEVEL% EQU 0 (..)”.
Curtis Yallop,
1
Znaleziono przypadki, w których %ERRORLEVEL%wynosi 0, mimo że wystąpił błąd. Zdarzyło się podczas %ERRORLEVEL%wpisywania pliku cmd. Próbowanie start /waitnie zadziałało. Jedyne, co zadziałało, toif errorlevel 1 (...)
AlikElzin-kilaka
1
Przyjazna rada:% ErrorLevel% jest zmienną powłoki, a nie zmienną środowiskową, a także zwraca wartość stringan int, co oznacza, że ​​nie można używać EQ/ NEQskutecznie.
kayleeFrye_onDeck
277

Testowanie ErrorLeveldziała dla aplikacji konsolowych , ale jak sugeruje dmihailescu , nie zadziała to, jeśli próbujesz uruchomić aplikację okienkową (np. Opartą na Win32) z wiersza poleceń. Aplikacja działająca w oknie uruchomi się w tle, a kontrola natychmiast powróci do wiersza poleceń (najprawdopodobniej z ErrorLevelzerą wskazującą, że proces został pomyślnie utworzony ). Gdy aplikacja okienkowa ostatecznie kończy działanie, jej status wyjścia zostaje utracony.

Zamiast korzystać z konsolowego programu uruchamiającego C ++ wspomnianego w innym miejscu, prostszą alternatywą jest uruchomienie aplikacji okienkowej za pomocą polecenia wiersza START /WAITpolecenia. Spowoduje to uruchomienie aplikacji okienkowej, poczekanie na jej zakończenie, a następnie przywrócenie sterowania do wiersza polecenia ze statusem zakończenia procesu ustawionym w ErrorLevel.

start /wait something.exe
echo %errorlevel%
Gary
źródło
20
Wielkie dzięki za pomysł „START / czekaj”. To działało dla mnie :)
Timotei
3
dobry chwyt. Nie wiedziałem o tym poleceniu. Właśnie widziałem, że działa dla> start / wait notepad.exe
dmihailescu,
1
Innym powodem, dla którego może nie działać (zawsze zero) jest to, że znajduje się w iflub for. Rozważ użycie !errorlevel!zamiast tego, jak opisano w tej odpowiedzi .
Roman Starkov
23

Jeśli chcesz dokładnie dopasować kod błędu (np. Wynosi 0), użyj tego:

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

if errorlevel 0dopasowania errorlevel> = 0. Patrz if /?.

Curtis Yallop
źródło
Czy wielkość liter ma znaczenie?
Nishant,
1
Nie. Zmienne, polecenia (w tym „if”) i „equ” działają bez względu na przypadek.
Curtis Yallop,
14

Może nie działać poprawnie, gdy używasz programu, który nie jest podłączony do konsoli, ponieważ ta aplikacja może nadal działać, gdy uważasz, że masz kod wyjścia. Rozwiązanie w C ++ wygląda następująco:

#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
źródło
W niektórych konfiguracjach należy dodać #include <atlstr.h>, aby rozpoznano typ CString.
Jake OPJ,
8

Warto zauważyć, że pliki .BAT i .CMD działają inaczej.

Czytając https://ss64.com/nt/errorlevel.html odnotowuje:

Istnieje zasadnicza różnica między sposobem ustawiania poziomów błędów w plikach wsadowych .CMD i .BAT:

Stary skrypt wsadowy .BAT uruchamiający „nowe” komendy wewnętrzne: APPEND, ASSOC, PATH, PROMPT, FTYPE i SET ustawią ERRORLEVEL tylko w przypadku wystąpienia błędu. Więc jeśli masz dwa polecenia w skrypcie wsadowym, a pierwsze nie powiedzie się, ERRORLEVEL pozostanie ustawiony nawet po udanym drugim poleceniu.

Może to utrudnić debugowanie skryptu BAT, skrypt wsadowy CMD jest bardziej spójny i ustawi ERRORLEVEL po każdej komendzie, którą uruchomisz [źródło].

Nie powodowało to końca smutku, gdy wykonywałem kolejne polecenia, ale ERRORLEVEL pozostałby niezmieniony nawet w przypadku awarii.

RockDoctor
źródło
0

W pewnym momencie musiałem dokładnie przesłać zdarzenia dziennika z Cygwin do dziennika zdarzeń Windows. Chciałem, aby komunikaty w WEVL były niestandardowe, miały poprawny kod wyjścia, szczegóły, priorytety, komunikat itp. Więc stworzyłem mały skrypt Bash, aby się tym zająć. Tutaj jest na GitHub, logit.sh .

Niektóre fragmenty:

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"

Oto część zawartości pliku tymczasowego:

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"

Oto funkcja tworzenia zdarzeń w 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
}

Wykonanie skryptu wsadowego i wywołanie __create_event:

cmd /c "$(cygpath -wa "$LGT_TEMP_FILE")"
__create_event
jonretting
źródło