timeout завершил проверку: отличаем лимит времени от ошибки команды

Пакетная проверка иногда ждёт ответа бесконечно. Инженер добавляет timeout, но затем любой ненулевой результат называет «истёкшим временем». В итоге настоящий отказ программы с кодом 2 уходит в повторные попытки, а пропавшая утилита тоже выглядит как медленная работа. Ограничить время недостаточно: нужно понять, кто сформировал код завершения.

GNU timeout запускает отдельную команду и при превышении срока посылает ей сигнал, по умолчанию TERM. Без --preserve-status истечение лимита обычно обозначается кодом 124. Если команда успела завершиться сама, возвращается её статус. Есть и собственные ошибки запуска timeout, которые требуют другой реакции.

Проверим границу без внешних сервисов

Вместо реальной проверки используем sleep. Этот пример запускает только короткий дочерний процесс и показывает, какая ветка выполнена.

#!/bin/sh
if timeout -k 1s 0.2s sleep 2; then
    printf '%s\n' 'Проверка завершилась вовремя'
else
    status=$?
    case "$status" in
        124) printf '%s\n' 'Достигнут лимит времени' ;;
        125|126|127) printf 'Не удалось запустить проверку: %s\n' "$status" >&2 ;;
        137) printf '%s\n' 'Получен KILL; нужен разбор причины' >&2 ;;
        *) printf 'Проверка завершилась с ошибкой: %s\n' "$status" >&2 ;;
    esac
fi

Ожидаемая ветка — «Достигнут лимит времени». Значение -k 1s задаёт дополнительный срок после первого сигнала: если процесс продолжит работать, последует KILL. Это не общий лимит в одну секунду от начала запуска. Для обычного sleep дополнительное принудительное завершение не понадобится.

Сроки меньше секунды удобны только для учебной проверки. В рабочем задании выбирайте интервал по нормальному времени операции и допустимой задержке результата. Слишком короткий лимит превращает обычные колебания времени в повторные ошибки и может увеличить нагрузку за счёт повторов.

Не путайте окончание ожидания с отменой действия

Завершение локальной программы не доказывает, что удалённая операция не состоялась. Если команда отправила запрос на публикацию, а ответ задержался, повтор способен создать дубль. Перед добавлением retry проверьте, есть ли у операции идентификатор запроса или безопасная проверка уже созданного результата.

Даже локальная команда может успеть записать часть файла. Для генерации отчёта пишите в отдельный кандидат и принимайте его только после успешного завершения. Сам факт существования выходного файла после timeout не означает, что файл закончен.

Код 137 означает получение KILL и не позволяет одним числом отличить все причины такого завершения. Не подписывайте его автоматически как «операция превысила лимит». Сохраните stderr, команду запуска без секретов и собственные временные отметки, чтобы разбирать обстоятельства, а не восстанавливать их по одному числу.

Выберите, что ограничивать

timeout запускает исполняемую программу, а не произвольную shell-строку. Если операция состоит из нескольких команд, вынесите её в отдельный скрипт либо явно вызовите shell с фиксированным текстом. Не склеивайте внешние данные в текст sh -c: передавайте их позиционными аргументами.

Для интерактивной программы существует --foreground, но при нём меняется управление группой процессов и дочерние процессы не охватываются тем же способом. Для пакетной проверки этот режим обычно не нужен. Перед применением к программе, порождающей свои дочерние задачи, проверьте её штатный механизм отмены и дождитесь завершения всех участников.

В курсе Linux и Bash скрипты строятся с проверкой ошибок. Откройте программу и продолжите к автоматизации, где ограничение времени, повтор и итоговый статус связаны с реальным состоянием операции, а не только с последней строкой лога.

Ещё по теме

Продолжить на практике

  • Linux и Bash для DevOps-инженеров ↗

    Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.