POSIX-эквивалент для тайм-аута GNU?

14

Команда GNU coreutils timeoutчрезвычайно удобна для определенных сценариев, позволяя использовать вывод команды, если она выполняется быстро, и пропускать ее, если это займет слишком много времени.

Как я могу приблизиться к базовому поведению timeoutиспользования только указанных POSIX утилит?


(Я имею в виду , что может включать в себя комбинацию wait, sleep, killи кто знает , что еще, но , возможно , я не хватает более легкий подход.)

Wildcard
источник
3
См. Раздел « Синхронизация в сценарии оболочки» , но я не считаю это дубликатом, потому что я запросил переносимость в системы, предшествующие POSIX, и у меня было требование сохранить stdin и stdout, что исключают некоторые решения, связанные с фоновыми процессами.
Жиль "ТАК - перестань быть злым"
command & pid=$! ; sleep 5 && kill $pid
Пандя
1
@Pandya, разве это не приводит к небольшому состоянию гонки, при котором, если он commandбыстро заканчивается, есть небольшая вероятность pidповторного использования другим процессом, killзапускающимся до запуска команды? Я не хотел бы этого в производственном коде ....
Wildcard
Можете ли вы объяснить больше о основной проблеме, которую вы пытаетесь решить? Почему бы просто не скомпилировать timeoutпрограмму и использовать ее?
Джеймс Янгман
2
@JamesYoungman, я думаю, вы не очень хорошо знаете, как писать скрипты для переносимости . Запрос о POSIX-совместимом (или указанном POSIX) способе выполнения чего-либо подразумевает, что он должен быть переносимым. Компиляция исходного кода в двоичный категорически не переносимо, и, в зависимости от вашей политики безопасности компании, вы не можете иметь компилятора установлен на все на сервере.
Wildcard

Ответы:

2

Мой подход будет таким:

  • Выполнить команду в качестве фонового процесса 1

  • Выполнить «сторожевой таймер» в качестве фонового процесса 2

  • Настройте обработчик для перехвата сигнала завершения в родительской оболочке.

  • Дождитесь завершения обоих процессов. Процесс, который завершается первым, отправляет сигнал завершения родителю.

  • Обработчик ловушек родителя уничтожает оба фоновых процесса с помощью управления заданиями (один из них уже завершен по определению, но это уничтожение будет безвредным, поскольку мы не используем PID, см. Ниже)

Я попытался обойти возможное состояние гонки, о котором говорится в комментариях, используя идентификаторы управления заданиями оболочки (которые были бы однозначными в этом экземпляре оболочки), чтобы идентифицировать фоновые процессы, которые нужно уничтожить, вместо системных PID.

#!/bin/sh

TIMEOUT=$1
COMMAND='sleep 5'

function cleanup {
    echo "SIGTERM trap"
    kill %1 %2
}

trap cleanup SIGTERM

($COMMAND; echo "Command completed"; kill $$) &
(sleep $TIMEOUT; echo "Timeout expired"; kill $$) &

wait
echo "End of execution"

Результат для TIMEOUT=10(команда завершается до сторожевого таймера):

$ ./timeout.sh 10
Command completed
SIGTERM trap
End of execution

Результат для TIMEOUT=1(сторожевой таймер останавливается перед командой):

$ ./timeout.sh 1
Timeout expired
SIGTERM trap
End of execution

Результат для TIMEOUT=5(сторожевой таймер и команда завершают работу «почти» одновременно):

./tst.sh 5
Timeout expired
Command completed
SIGTERM trap
End of execution
Гвидо
источник
Это не соответствует POSIX, так как (a) определение функции должно быть cleanup() { ...; }и (b) управление заданиями является функцией bash, не указанной в POSIX (хотя ksh и другие также имеют ее). Хороший сценарий, хотя!
Wildcard
Вы можете также сделать лучше timeout="$1"; shiftи (exec "$@"; echo "Command completed"; kill $$)лучше , чем повторно wordsplitting список аргументов.
Wildcard