Comment puis-je obtenir le code de sortie de l'application à partir d'une ligne de commande Windows?

797

J'exécute un programme et je veux voir quel est son code de retour (car il renvoie différents codes en fonction d'erreurs différentes).

Je sais que dans Bash, je peux le faire en exécutant

écho $?

Que dois-je faire lors de l'utilisation de cmd.exe sous Windows?

Skrud
la source
1
Googlé pour "Win8 Comment obtenir l'invite CMD pour afficher l'état de sortie" comme nous pouvons le faire sous Linux. C'était la meilleure sélection et elle est exacte.
SDsolar
1
Vous pouvez rapidement voir ce que l'application retourne:app.exe & echo %errorlevel%
marbel82

Réponses:

976

Une pseudo variable d'environnement nommée errorlevelstocke le code de sortie:

echo Exit Code is %errorlevel%

De plus, la ifcommande a une syntaxe spéciale:

if errorlevel

Voir if /?pour plus de détails.

Exemple

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

Avertissement: Si vous définissez un nom de variable d'environnement errorlevel, %errorlevel%retournera cette valeur et non le code de sortie. Utilisez ( set errorlevel=) pour effacer la variable d'environnement, permettant d'accéder à la vraie valeur de errorlevelvia la %errorlevel%variable d'environnement.

DrFloyd5
la source
38
Si vous exécutez directement à partir d'une ligne de commande Windows et que vous voyez toujours 0 renvoyé, consultez la réponse de Gary: stackoverflow.com/a/11476681/31629
Ken
9
Aussi, si vous êtes en PowerShell, vous pouvez utiliserecho Exit Code is $LastExitCode
Brandon Pugh
11
Remarque: "errorlevel 1" est vrai si errorlevel> = 1. Donc "errorlevel 0" correspondra à tout. Voir si /?". À la place, vous pouvez utiliser "if% ERRORLEVEL% EQU 0 (..)".
Curtis Yallop
1
Cas trouvés où %ERRORLEVEL%est 0 même si une erreur s'est produite. Arrivé lors de l'archivage %ERRORLEVEL%d'un fichier cmd. Essayer start /waitn'a pas fonctionné. La seule chose qui a fonctionné estif errorlevel 1 (...)
AlikElzin-kilaka
1
Conseil convivial:% ErrorLevel% est une variable shell, pas une variable d'environnement, et elle renvoie également un stringnot an int, ce qui signifie que vous ne pouvez pas utiliser EQ/ NEQefficacement.
kayleeFrye_onDeck
277

Les tests ErrorLevelfonctionnent pour les applications de console , mais comme l' indique dmihailescu , cela ne fonctionnera pas si vous essayez d'exécuter une application fenêtrée (par exemple basée sur Win32) à partir d'une invite de commande. Une application fenêtrée s'exécutera en arrière-plan et le contrôle reviendra immédiatement à l'invite de commande (très probablement avec un ErrorLevelzéro pour indiquer que le processus a été créé avec succès). Lorsqu'une application fenêtrée se ferme, son état de sortie est perdu.

Au lieu d'utiliser le lanceur C ++ sur console mentionné ailleurs, cependant, une alternative plus simple consiste à démarrer une application fenêtrée à l'aide de la commande de l'invite de START /WAITcommande. Cela démarrera l'application fenêtrée, attendra sa fermeture, puis renverra le contrôle à l'invite de commandes avec l'état de sortie du processus défini dans ErrorLevel.

start /wait something.exe
echo %errorlevel%
Gary
la source
20
Merci beaucoup pour l'idée "START / wait". Cela a fonctionné pour moi :)
Timotei
3
Belle prise. Je ne connaissais pas cette commande. Je viens de le voir fonctionner pour> start / wait
notepad.exe
1
Une autre raison pour laquelle cela pourrait ne pas fonctionner (toujours zéro) est lorsqu'elle se trouve à l'intérieur d'un ifou for. Pensez à utiliser à la !errorlevel!place, comme décrit dans cette réponse .
Roman Starkov
23

Si vous souhaitez faire correspondre exactement le code d'erreur (par exemple, égal à 0), utilisez ceci:

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

if errorlevel 0correspond à errorlevel> = 0. Voir if /?.

Curtis Yallop
la source
Est-il sensible à la casse?
Nishant
1
Non. Les variables, les commandes (y compris "si") et "equ" fonctionnent quel que soit le cas.
Curtis Yallop
14

Cela peut ne pas fonctionner correctement lorsque vous utilisez un programme qui n'est pas attaché à la console, car cette application peut toujours être en cours d'exécution alors que vous pensez avoir le code de sortie. Une solution pour le faire en C ++ ressemble à ci-dessous:

#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
la source
Dans certaines configurations, vous devez ajouter #include <atlstr.h> afin que le type CString soit reconnu.
Jake OPJ
8

Il convient de noter que les fichiers .BAT et .CMD fonctionnent différemment.

En lisant https://ss64.com/nt/errorlevel.html, il note les éléments suivants:

Il existe une différence essentielle entre la façon dont les fichiers batch .CMD et .BAT définissent les niveaux d'erreur:

Un ancien script batch .BAT exécutant les «nouvelles» commandes internes: APPEND, ASSOC, PATH, PROMPT, FTYPE et SET ne définira ERRORLEVEL qu'en cas d'erreur. Donc, si vous avez deux commandes dans le script de commandes et que la première échoue, ERRORLEVEL restera défini même après la réussite de la deuxième commande.

Cela peut rendre le débogage d'un script BAT problème plus difficile, un script batch CMD est plus cohérent et définira ERRORLEVEL après chaque commande que vous exécutez [source].

Cela ne me causait pas de chagrin pendant que j'exécutais des commandes successives, mais le NIVEAU D'ERREUR resterait inchangé même en cas de panne.

RockDoctor
la source
0

À un moment donné, j'avais besoin de pousser avec précision les événements du journal de Cygwin vers le journal des événements Windows. Je voulais que les messages dans WEVL soient personnalisés, aient le bon code de sortie, les détails, les priorités, le message, etc. J'ai donc créé un petit script Bash pour m'en occuper. Le voici sur GitHub, logit.sh .

Quelques extraits:

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"

Voici la partie du contenu du fichier temporaire:

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"

Voici une fonction pour créer des événements dans 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
}

Exécuter le script batch et appeler __create_event:

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