CLI приложения #5. Коды возврата
Наше приложение уже умеет команды, опции и аргументы. Но снаружи оно всегда выглядит успешным — что бы внутри ни случилось.
Проверить вывод можно так: запустите приложение с заведомо неверными аргументами, а следом:
# если PowerShell
$LASTEXITCODE
# в bash то же самое:
echo $?
Получите возврата: целое число, которое процесс отдаёт операционной системе при завершении. Ноль — всё хорошо, любое другое значение — что-то пошло не так. Больше программа сказать не может: ни текста, ни объекта, только число.
Казалось бы, мелочь. Но именно на этом числе держится вся автоматизация:
# запустить второе, только если первое отработало успешно
myapp build && myapp deploy
# CI упадёт на этом шаге, если код возврата не ноль
- run: myapp validate config.json
Если приложение всегда возвращает ноль, то в CI пайплайне всё будет хорошо, а проблемы всплывут уже в продакшене.
В C# код возврата — это то, что вернул Main. Поэтому сигнатура static int Main, а не void. В System.CommandLine число возвращается из SetAction:
internal static class ExitCode
{
public const int Success = 0;
public const int UnknownError = 1;
public const int FileNotFound = 2;
public const int InvalidFormat = 3;
}
Применяем в коде:
validateCommand.SetAction(parseResult =>
{
var path = parseResult.GetValue(pathArgument);
if (!File.Exists(path))
{
Console.Error.WriteLine($"Файл не найден: {path}");
return ExitCode.FileNotFound;
}
try
{
JsonDocument.Parse(File.ReadAllText(path));
}
catch (JsonException ex)
{
Console.Error.WriteLine($"Некорректный JSON: {ex.Message}");
return ExitCode.InvalidFormat;
}
Console.WriteLine("Файл корректен");
return ExitCode.Success;
});
Важно!
Необработанное исключение тоже завершит процесс ненулевым кодом, но каким именно — зависит от среды выполнения, и рассчитывать на это число нельзя. Поэтому ловите исключения и возвращайте свой код.
Несколько моментов, которые стоит знать заранее:
- Диапазон — от 0 до 255. На
Unixчисло берётся по модулю 256, так чтоreturn 256превратится в ноль, то есть в «успех».- Коды 126, 127, 128 и 130 заняты оболочкой (нет прав на запуск, команда не найдена, прерывание по
Ctrl+C). Так что свои коды лучше выбирать в пределах 1–125.- Опишите значения кодов в справке или
README. Пользователь, который пишет обвязку вокруг вашей утилиты, скажет спасибо. Ну или хотя бы не будет неприятного удивления
Кстати, обратите внимание, что в примере ошибки печатаются через Console.Error, а не привычный Console.WriteLine. Это не опечатка. Про два потока вывода поговорим в следующем посте.