it-swarm.it

Come ottengo il codice di uscita dell'applicazione da una riga di comando di Windows?

Sto eseguendo un programma e voglio vedere qual è il suo codice di ritorno (dal momento che restituisce codici diversi in base a diversi errori).

So che in Bash posso farlo correndo

echo $?

Cosa faccio quando utilizzo cmd.exe su Windows?

708
Skrud

Una pseudo variabile d'ambiente chiamata errorlevel memorizza il codice di uscita:

echo Exit Code is %errorlevel%

Inoltre, il comando if ha una sintassi speciale:

if errorlevel

Vedi if /? per i dettagli.

Esempio

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

Avviso: se si imposta un nome di variabile di ambiente errorlevel, %errorlevel% restituirà tale valore e non il codice di uscita. Utilizzare (set errorlevel=) per cancellare la variabile di ambiente, consentendo l'accesso al valore reale di errorlevel tramite la variabile di ambiente %errorlevel%.

863
Samuel Renkert

Il test ErrorLevel funziona con console applications, ma come suggerito da da dmihailescu , questo non funzionerà se si sta tentando di eseguire un windowed application (ad es. Win32-based ) da un prompt dei comandi. Un'applicazione con finestre verrà eseguita in background e il controllo tornerà immediatamente al comando Prompt (molto probabilmente con un ErrorLevel di zero per indicare che il processo era created successfully). Alla fine della chiusura di un'applicazione, il suo stato di uscita viene perso.

Invece di usare il launcher C++ basato su console menzionato altrove, però, un'alternativa più semplice è avviare un'applicazione windowed usando il comando START /WAIT del prompt Prompt. Ciò avvierà l'applicazione a finestre, attenderà che esca e quindi restituirà il controllo al comando Prompt con lo stato di uscita del processo impostato in ErrorLevel.

start /wait something.exe
echo %errorlevel%
227
Gary

Usa la variabile ERRORLEVEL integrata:

echo %ERRORLEVEL%

Ma attenzione se un'applicazione ha definito una variabile d'ambiente chiamata ERRORLEVEL !

92
Adam Rosenfield

Se vuoi far corrispondere esattamente il codice di errore (eg uguale a 0), usa questo:

@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 corrisponde a errorlevel> = 0. Vedi if /?.

18
Curtis Yallop

Potrebbe non funzionare correttamente quando si utilizza un programma che non è collegato alla console, perché l'app potrebbe essere ancora in esecuzione mentre si pensa di avere il codice di uscita. Una soluzione per farlo in C++ è la seguente:

#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;
}
14
dmihailescu

Vale la pena notare che i file .BAT e .CMD operano in modo diverso.

Lettura https://ss64.com/nt/errorlevel.html osserva quanto segue:

C'è una differenza fondamentale tra il modo in cui i file batch .CMD e .BAT impostano i livelli di errore:

Un vecchio script batch .BAT che esegue i "nuovi" comandi interni: APPEND, ASSOC, PATH, Prompt, FTYPE e SET imposteranno ERRORLEVEL solo se si verifica un errore. Quindi se hai due comandi nello script batch e il primo fallisce, ERRORLEVEL rimarrà impostato anche dopo il completamento del secondo comando.

Ciò può rendere più difficile il debugging di uno script BAT, uno script batch CMD è più coerente e imposterà ERRORLEVEL dopo ogni comando eseguito [source].

Questo mi stava causando la fine del dolore dato che stavo eseguendo comandi successivi, ma ERRORLEVEL sarebbe rimasto invariato anche in caso di fallimento.

5
RockDoctor

A un certo punto avevo bisogno di spingere accuratamente gli eventi di registro da Cygwin al registro eventi di Windows. Volevo che i messaggi in WEVL fossero personalizzati, che avessero il codice di uscita corretto, i dettagli, le priorità, il messaggio, ecc. Così ho creato un piccolo script di Bash per occuparmi di questo. Eccolo su GitHub, logit.sh .

Alcuni estratti:

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"

Ecco la parte del contenuto del file temporaneo:

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"

Ecco una funzione per creare eventi 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
}

Esecuzione dello script batch e richiamo su __create_event:

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