Как получить код завершения приложения из командной строки Windows?
Я запускаю программу и хочу посмотреть, каков ее код возврата (так как он возвращает разные коды на основе разных ошибок).
Я знаю, в Баш я могу сделать это, запустив
echo $?
Что мне делать при использовании cmd.exe в Windows?
7 ответов
Псевдо-переменная среды с именем errorlevel
сохраняет код выхода:
echo Exit Code is %errorlevel%
Так же if
Команда имеет специальный синтаксис:
if errorlevel
Увидеть if /?
для деталей.
пример
@echo off
my_nify_exe.exe
if errorlevel 1 (
echo Failure Reason Given is %errorlevel%
exit /b %errorlevel%
)
Предупреждение: если вы задали имя переменной среды errorlevel
, %errorlevel%
вернет это значение, а не код выхода. Использовать ( set errorlevel=
) очистить переменную среды, предоставив доступ к истинному значению errorlevel
через %errorlevel%
переменная окружения.
Тестирование ErrorLevel
работает для консольных приложений, но, как намекнул dmihailescu, это не сработает, если вы пытаетесь запустить оконное приложение (например, на основе Win32) из командной строки. Оконное приложение будет работать в фоновом режиме, и элемент управления немедленно вернется в командную строку (скорее всего, с ErrorLevel
нуля, чтобы указать, что процесс был создан успешно). Когда оконное приложение в конечном итоге завершает работу, его состояние выхода теряется.
Вместо того, чтобы использовать консольный модуль запуска C++, упомянутый в другом месте, более простой альтернативой является запуск оконного приложения с использованием командной строки. START /WAIT
команда. Это запустит оконное приложение, дождется его завершения и вернет управление в командную строку со статусом завершения процесса, установленным в ErrorLevel
,
start /wait something.exe
echo %errorlevel%
Используйте встроенную переменную ERRORLEVEL:
echo %ERRORLEVEL%
Но будьте осторожны, если приложение определило переменную среды с именем ERRORLEVEL!
Если вы хотите точно соответствовать коду ошибки (например, равен 0), используйте это:
@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
Матчи errorlevel
>= 0. См. if /?
,
Стоит отметить, что файлы.BAT и.CMD работают по-разному.
Читая https://ss64.com/nt/errorlevel.html он отмечает следующее:
Существует ключевое различие между тем, как пакетные файлы.CMD и.BAT устанавливают уровни ошибок:
Старый пакетный скрипт.BAT, выполняющий "новые" внутренние команды: APPEND, ASSOC, PATH, PROMPT, FTYPE и SET, будет только устанавливать ERRORLEVEL в случае возникновения ошибки. Таким образом, если у вас есть две команды в пакетном скрипте, и первая не выполняется, ERRORLEVEL останется установленным даже после успешного выполнения второй команды.
Это может усложнить отладку проблемного сценария BAT, пакетный сценарий CMD более согласован и будет устанавливать значение ERRORLEVEL после каждой команды, которую вы запускаете [source].
Это не давало мне конца печали, когда я выполнял последовательные команды, но ОШИБКА оставалась неизменной даже в случае сбоя.
Он может работать некорректно при использовании программы, которая не подключена к консоли, потому что это приложение может все еще работать, пока вы думаете, что у вас есть код выхода. Решение сделать это в C++ выглядит следующим образом:
#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;
}
В какой-то момент мне нужно было точно перенести события журнала из Cygwin в журнал событий Windows. Я хотел, чтобы сообщения в WEVL были нестандартными, имели правильный код завершения, детали, приоритеты, сообщения и т. Д. Поэтому я создал небольшой скрипт Bash, чтобы позаботиться об этом. Вот это на GitHub, logit.sh.
Некоторые выдержки:
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"
Вот часть содержимого временного файла:
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"
Вот функция для создания событий в 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
}
Выполнение пакетного сценария и вызов __create_event:
cmd /c "$(cygpath -wa "$LGT_TEMP_FILE")"
__create_event