¿Cómo obtengo el código de salida de la aplicación desde una línea de comandos de Windows?

797

Estoy ejecutando un programa y quiero ver cuál es su código de retorno (ya que devuelve diferentes códigos basados ​​en diferentes errores).

Sé que en Bash puedo hacer esto ejecutando

echo $?

¿Qué hago cuando uso cmd.exe en Windows?

Skrud
fuente
1
Busqué en Google "Win8 Cómo obtener el indicador CMD para mostrar el estado de salida" como podemos hacer en Linux. Esta fue la mejor selección, y es precisa.
SDsolar
1
Puede ver rápidamente qué aplicación regresa:app.exe & echo %errorlevel%
marbel82

Respuestas:

976

Una pseudo variable de entorno llamada errorlevelalmacena el código de salida:

echo Exit Code is %errorlevel%

Además, el ifcomando tiene una sintaxis especial:

if errorlevel

Ver if /?para más detalles.

Ejemplo

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

Advertencia: si establece un nombre de variable de entorno errorlevel, %errorlevel%devolverá ese valor y no el código de salida. Use ( set errorlevel=) para borrar la variable de entorno, permitiendo el acceso al valor verdadero de a errorleveltravés de la %errorlevel%variable de entorno

DrFloyd5
fuente
38
Si está ejecutando directamente desde una línea de comandos de Windows y siempre ve 0 devuelto, vea la respuesta de Gary: stackoverflow.com/a/11476681/31629
Ken
99
Además, si está en PowerShell, puede usarloecho Exit Code is $LastExitCode
Brandon Pugh el
11
Nota: "errorlevel 1" es verdadero si errorlevel> = 1. Entonces "errorlevel 0" coincidirá con todo. Ve si /?". En su lugar, puede usar "if% ERRORLEVEL% EQU 0 (..)".
Curtis Yallop
1
Se encontraron casos donde %ERRORLEVEL%es 0 a pesar de que ocurrió un error. Sucedió al registrar %ERRORLEVEL%un archivo cmd. Intentar start /waitno funcionó. Lo único que funcionó esif errorlevel 1 (...)
AlikElzin-kilaka
1
Consejo amigable:% ErrorLevel% es una variable de shell, no una variable de entorno, y también devuelve un stringno int, lo que significa que no puede usar EQ/ NEQefectivamente.
kayleeFrye_onDeck
277

Las pruebas ErrorLevelfuncionan para aplicaciones de consola , pero como lo indica dmihailescu , esto no funcionará si está intentando ejecutar una aplicación con ventana (por ejemplo, basada en Win32) desde un símbolo del sistema. Una aplicación con ventana se ejecutará en segundo plano, y el control volverá inmediatamente al símbolo del sistema (muy probablemente con un ErrorLevelcero para indicar que el proceso se creó con éxito). Cuando una aplicación con ventana finalmente sale, su estado de salida se pierde.

Sin embargo, en lugar de usar el iniciador de C ++ basado en consola mencionado en otra parte, una alternativa más simple es iniciar una aplicación en ventana usando el comando del símbolo del sistema START /WAIT. Esto iniciará la aplicación en ventana, esperará a que salga y luego devolverá el control al símbolo del sistema con el estado de salida del proceso establecido ErrorLevel.

start /wait something.exe
echo %errorlevel%
Gary
fuente
20
Muchas gracias por la idea "START / wait". Eso funcionó para mí :)
Timotei
3
buena atrapada. No sabía sobre ese comando. Yo sólo he visto trabajar para> start / wait notepad.exe
dmihailescu
1
Otra razón por la que podría no funcionar (siempre cero) es cuando está dentro de un ifo for. Considere usar en su !errorlevel!lugar, como se describe en esta respuesta .
Roman Starkov
23

Si desea coincidir exactamente con el código de error (por ejemplo, igual a 0), use esto:

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

if errorlevel 0coincidencias errorlevel> = 0. Ver if /?.

Curtis Yallop
fuente
¿Es sensible a mayúsculas y minúsculas?
Nishant
1
No. vars, comandos (incluyendo "if") y "equ" funcionan sin importar el caso.
Curtis Yallop
14

Es posible que no funcione correctamente cuando se utiliza un programa que no está conectado a la consola, porque esa aplicación aún puede estar ejecutándose mientras cree que tiene el código de salida. Una solución para hacerlo en C ++ se ve a continuación:

#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
fuente
En algunas configuraciones, debe agregar #include <atlstr.h> para que se reconozca el tipo CString.
Jake OPJ
8

Vale la pena señalar que los archivos .BAT y .CMD funcionan de manera diferente.

Al leer https://ss64.com/nt/errorlevel.html , observa lo siguiente:

Hay una diferencia clave entre la forma en que los archivos por lotes .CMD y .BAT establecen niveles de error:

Un antiguo script por lotes .BAT que ejecuta los comandos internos 'nuevos': APPEND, ASSOC, PATH, PROMPT, FTYPE y SET solo establecerá ERRORLEVEL si se produce un error. Entonces, si tiene dos comandos en el script por lotes y el primero falla, el ERRORLEVEL permanecerá establecido incluso después de que el segundo comando tenga éxito.

Esto puede dificultar la depuración de un script BAT problemático, un script por lotes CMD es más consistente y establecerá ERRORLEVEL después de cada comando que ejecute [fuente].

Esto me estaba causando un sinfín de penas ya que estaba ejecutando comandos sucesivos, pero el ERRORLEVEL permanecería sin cambios incluso en caso de falla.

RockDoctor
fuente
0

En un momento, necesitaba insertar con precisión los eventos de registro de Cygwin en el registro de eventos de Windows. Quería que los mensajes en WEVL fueran personalizados, que tuvieran el código de salida correcto, detalles, prioridades, mensajes, etc. Así que creé un pequeño script Bash para encargarme de esto. Aquí está en GitHub, logit.sh .

Algunos extractos:

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"

Aquí está la parte del contenido del archivo temporal:

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"

Aquí hay una función para crear eventos en 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
}

Ejecutando el script por lotes y llamando a __create_event:

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