Como obtenho o código de saída do aplicativo em uma linha de comando do Windows?

797

Estou executando um programa e quero ver qual é o seu código de retorno (pois ele retorna códigos diferentes com base em erros diferentes).

Eu sei que no Bash eu posso fazer isso executando

eco $?

O que faço ao usar o cmd.exe no Windows?

Skrud
fonte
1
Pesquisou no "Win8 Como obter o prompt do CMD para mostrar o status da saída", como podemos fazer no Linux. Esta foi a melhor seleção e é precisa.
SDsolar 21/05
1
Você pode ver rapidamente o aplicativo retornado:app.exe & echo %errorlevel%
marbel82 19/02/19

Respostas:

976

Uma pseudo variável de ambiente chamada errorlevelarmazena o código de saída:

echo Exit Code is %errorlevel%

Além disso, o ifcomando possui uma sintaxe especial:

if errorlevel

Veja if /?para detalhes.

Exemplo

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

Aviso: Se você definir um nome de variável de ambiente errorlevel, %errorlevel%retornará esse valor e não o código de saída. Use ( set errorlevel=) para limpar a variável de ambiente, permitindo acesso ao valor real da errorlevelvia %errorlevel%variável de ambiente.

DrFloyd5
fonte
38
Se você estiver executando diretamente a partir de uma linha de comando do Windows e sempre vendo 0 voltou, ver a resposta de Gary: stackoverflow.com/a/11476681/31629
Ken
9
Além disso, se você estiver em PowerShell que você pode usarecho Exit Code is $LastExitCode
Brandon Pugh
11
Nota: "errorlevel 1" é verdadeiro se errorlevel> = 1. Portanto, "errorlevel 0" corresponderá a tudo. Veja se /?". Em vez disso, você pode usar "se% ERRORLEVEL% EQU 0 (..)".
Curtis Yallop 29/07
1
Casos encontrados em que %ERRORLEVEL%0 é o mesmo que ocorreu um erro. Aconteceu ao fazer o check- %ERRORLEVEL%in de um arquivo cmd. Tentar start /waitnão funcionou. A única coisa que funcionou éif errorlevel 1 (...)
AlikElzin-kilaka 13/04/2015
1
Conselho amigável:% ErrorLevel% é uma variável do shell, não uma variável de ambiente, e também retorna um stringnão int, o que significa que você não pode usar EQ/ NEQefetivamente.
precisa saber é o seguinte
277

O teste ErrorLevelfunciona para aplicativos de console , mas como sugerido pelo dmihailescu , isso não funcionará se você estiver tentando executar um aplicativo em janela (por exemplo, baseado em Win32) em um prompt de comando. Um aplicativo em janela será executado em segundo plano e o controle retornará imediatamente ao prompt de comando (provavelmente com ErrorLevelzero para indicar que o processo foi criado com êxito). Quando um aplicativo em janela eventualmente sai, seu status de saída é perdido.

Em vez de usar o iniciador de C ++ baseado em console mencionado em outro lugar, uma alternativa mais simples é iniciar um aplicativo em janela usando o comando do prompt de START /WAITcomando. Isso iniciará o aplicativo em janela, espere que ele saia e retorne o controle ao prompt de comando com o status de saída do processo definido ErrorLevel.

start /wait something.exe
echo %errorlevel%
Gary
fonte
20
Muito obrigado pela ideia "START / wait". Isso funcionou para mim:) #
214 Timotei
3
boa pegada. Eu não sabia sobre esse comando. Acabei de vê-lo trabalhando para> start / wait notepad.exe
dmihailescu
1
Outra razão pela qual ele pode não funcionar (sempre zero) é quando está dentro de um ifou for. Considere usar !errorlevel!, como descrito nesta resposta .
Roman Starkov
23

Se você deseja corresponder exatamente ao código de erro (por exemplo, igual a 0), use o seguinte:

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

if errorlevel 0corresponde a errorlevel> = 0. Veja if /?.

Curtis Yallop
fonte
Faz distinção entre maiúsculas e minúsculas?
Nishant
1
Não. Vars, comandos (incluindo "if") e "equ" funcionam, não importa qual seja o caso.
Curtis Yallop
14

Pode não funcionar corretamente ao usar um programa que não está conectado ao console, porque esse aplicativo ainda pode estar em execução enquanto você acha que possui o código de saída. Uma solução para fazê-lo em C ++ é apresentada abaixo:

#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
fonte
Em algumas configurações, você deve adicionar #include <atlstr.h> para que o tipo CString seja recongnizado.
Jake OPJ
8

Vale a pena notar que os arquivos .BAT e .CMD funcionam de maneira diferente.

Lendo https://ss64.com/nt/errorlevel.html , observa o seguinte:

Há uma diferença importante entre a maneira como os arquivos em lotes .CMD e .BAT definem os níveis de erro:

Um script em lote .BAT antigo executando os 'novos' comandos internos: APPEND, ASSOC, PATH, PROMPT, FTYPE e SET somente definirão ERRORLEVEL se ocorrer um erro. Portanto, se você tiver dois comandos no script em lote e o primeiro falhar, o ERRORLEVEL permanecerá definido mesmo depois que o segundo comando for bem-sucedido.

Isso pode dificultar a depuração de um script BAT com problema, um script em lote CMD é mais consistente e definirá ERRORLEVEL após cada comando que você executa [origem].

Isso estava me causando dor de cabeça, enquanto eu executava comandos sucessivos, mas o ERRORLEVEL permaneceria inalterado mesmo no caso de uma falha.

RockDoctor
fonte
0

Em um ponto, eu precisava enviar com precisão os eventos de log do Cygwin para o log de eventos do Windows. Eu queria que as mensagens no WEVL fossem personalizadas, tivessem o código de saída correto, detalhes, prioridades, mensagem, etc. Então, criei um pequeno script Bash para cuidar disso. Aqui está no GitHub, logit.sh .

Alguns trechos:

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"

Aqui está a parte do conteúdo do arquivo temporário:

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"

Aqui está uma função para criar eventos no 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
}

Executando o script em lote e chamando __create_event:

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