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?
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.
@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%
.
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%
Usa la variabile ERRORLEVEL integrata:
echo %ERRORLEVEL%
Ma attenzione se un'applicazione ha definito una variabile d'ambiente chiamata ERRORLEVEL !
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 /?
.
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;
}
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.
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