web-dev-qa-db-fra.com

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

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

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

echo $?

Que dois-je faire lorsque j'utilise cmd.exe sous Windows?

737
Skrud

Une pseudo-variable d’environnement nommée errorlevel stocke le code de sortie:

echo Exit Code is %errorlevel%

De plus, la commande if 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% renverra cette valeur et non le code de sortie. Utilisez (set errorlevel=) pour effacer la variable d'environnement, permettant ainsi l'accès. à la valeur vraie de errorlevel via la variable d’environnement %errorlevel%.

899
Samuel Renkert

Tester ErrorLevel fonctionne pour les applications de la console , mais comme le suggère de dmihailesc , cela ne fonctionnera pas si vous essayez d'exécuter une application fenêtrée (p. ex. basée sur Win32) à partir d'une invite de commande. Une application fenêtrée sera exécutée en arrière-plan et le contrôle retournera immédiatement à la commande Invite (le plus souvent avec un ErrorLevel égal à zéro pour indiquer que le processus a été créé avec succès). Lorsqu'une application fenêtrée se ferme, son statut de sortie est perdu.

Au lieu d'utiliser le lanceur C++ basé sur la console mentionné ailleurs, une alternative plus simple consiste à démarrer une application fenêtrée à l'aide de la commande Prompt's START /WAIT. Cela va démarrer l'application fenêtrée, attendre qu'elle se ferme, puis renvoyer le contrôle à l'invite de commande avec le statut de sortie du processus défini dans ErrorLevel.

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

Utilisez la variable ERRORLEVEL intégrée:

echo %ERRORLEVEL%

Mais attention si une application a défini une variable d'environnement nommée ERRORLEVEL !

94
Adam Rosenfield

Si vous voulez que le code d'erreur corresponde exactement (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 0 correspond à errorlevel> = 0. Voir if /?.

20
Curtis Yallop

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

#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

Il est à noter que les fichiers .BAT et .CMD fonctionnent différemment.

La lecture https://ss64.com/nt/errorlevel.html indique les points suivants:

Il existe une différence essentielle entre la manière dont les fichiers de commandes .CMD et .BAT définissent les niveaux d'erreur:

Un ancien script de lot .BAT exécutant les "nouvelles" commandes internes: APPEND, ASSOC, PATH, Prompt, FTYPE et SET ne définira ERRORLEVEL que si une erreur se produit. Ainsi, si vous avez deux commandes dans le script batch et que la première échoue, ERRORLEVEL restera activé même après le succès de la deuxième commande.

Cela peut rendre plus difficile le débogage d'un script BAT problématique. Un script de traitement par lots CMD est plus cohérent et définit ERRORLEVEL après chaque commande que vous exécutez [source].

Cela me causait beaucoup de chagrin car j'exécutais des commandes successives, mais ERRORLEVEL resterait inchangé même en cas d'échec.

6
RockDoctor

À un moment donné, j'avais besoin de transmettre avec précision les événements de journal de Cygwin au journal des événements Windows. Je voulais que les messages dans WEVL soient personnalisés, que le code de sortie, les détails, les priorités, le message, etc. soient corrects. J'ai donc créé un petit script Bash pour prendre soin de cela. La 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 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écution du script de traitement par lots et appel à __create_event:

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