У меня есть сценарий оболочки, который читает со стандартного ввода . В редких случаях никто не будет готов предоставить данные, и сценарий должен отключиться . В случае тайм-аута скрипт должен выполнить некоторый код очистки. Какой лучший способ сделать это?
Этот сценарий должен быть очень переносимым , в том числе для Unix-систем 20-го века без компилятора C и для встраиваемых устройств, работающих под управлением busybox, поэтому нельзя полагаться на Perl, bash, любой скомпилированный язык и даже полную версию POSIX.2. В частности, $PPID
, read -t
и отлично POSIX-совместимые ловушки не доступны. Запись во временный файл также исключена; скрипт может работать, даже если все файловые системы смонтированы только для чтения.
Просто чтобы сделать вещи более сложными, я также хочу, чтобы сценарий был достаточно быстрым, когда он не истекает. В частности, я также использую скрипт в Windows (в основном в Cygwin), где fork и exec особенно низки, поэтому я хочу свести их использование к минимуму.
Короче говоря, у меня есть
trap cleanup 1 2 3 15
foo=`cat`
и я хочу добавить тайм-аут. Я не могу заменить cat
на read
встроенный. В случае тайм-аута я хочу выполнить cleanup
функцию.
Справочная информация: этот скрипт угадывает кодировку терминала, печатая некоторые 8-битные символы и сравнивая положение курсора до и после. В начале скрипта проверяется, что stdout подключен к поддерживаемому терминалу, но иногда среда врет (например, plink
устанавливает, TERM=xterm
даже если онTERM=dumb
вызывается с помощью ). Соответствующая часть скрипта выглядит так:
text='Éé' # UTF-8; shows up as Ãé on a latin1 terminal
csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n" # ␛ is an escape character
stty_save=`stty -g`
cleanup () { stty "$stty_save"; }
trap 'cleanup; exit 120' 0 1 2 3 15 # cleanup code
stty eol 0 eof n -echo # Input will end with `0n`
# echo-n is a function that outputs its argument without a newline
echo-n "$dsr_cpr$dsr_ok" # Ask the terminal to report the cursor position
initial_report=`tr -dc \;0123456789` # Expect ␛[42;10R␛[0n for y=42,x=10
echo-n "$text$dsr_cpr$dsr_ok"
final_report=`tr -dc \;0123456789`
cleanup
# Compute and return initial_x - final_x
Как я могу изменить сценарий так, чтобы, если tr
через 2 секунды не было прочитано ни одного ввода, он был уничтожен и сценарий выполнил cleanup
функцию?
Ответы:
Как насчет этого:
То есть: запустите выходную команду и
sleep
в той же группе процессов группу процессов только для них. Какая бы команда не вернулась первой, она уничтожает всю группу процессов.Кто-нибудь задумался: да, труба не используется; это обойдено с помощью перенаправлений. Единственная цель состоит в том, чтобы оболочка запускала два процесса в одной и той же группе процессов.
Как отметил Жиль в своем комментарии, это не будет работать в сценарии оболочки, потому что процесс сценария будет уничтожен вместе с двумя подпроцессами.
Один из способов заставить команду работать в отдельной группе процессов - это запустить новую интерактивную оболочку:
Но с этим могут быть оговорки (например, когда stdin не tty?). Перенаправление stderr позволяет избавиться от сообщения «Прекращено», когда интерактивная оболочка уничтожена.
Проверено с
zsh
,bash
иdash
. Но как насчет старых?B98 предлагает следующее изменение, работающее на Mac OS X с GNU bash 3.2.57 или Linux с dash:
-
1. кроме того,
setsid
что кажется нестандартным.источник
kill 0
конечном итоге также убивает вызывающего сценарий. Существует ли переносимый способ принудительного запуска конвейера в собственную группу процессов?setprgp()
безsetsid
пока :-(Вы уже в ловушке 15 (числовая версия
SIGTERM
, котораяkill
отправляет, если не указано иное), так что вы уже должны быть готовы. Тем не менее, если вы смотрите на pre-POSIX, имейте в виду, что функции оболочки также могут не существовать (они пришли из оболочки System V).источник
cat
выхода. Я уже немного поэкспериментировал, но пока не нашел ничего, чем доволен.$!
, я думаю, что некоторые из тех машин, которые я использую редко, не имеют контроля работы,$!
доступны ли они повсеместно?bash
специфические вещи, которые я когда-то делал, связанные со злоупотреблениями-o monitor
. Когда я писал об этом, я думал о действительно древних оболочках (это работало в v7). Тем не менее, я думаю, что вы можете сделать одну из двух других вещей: (1) фон «что угодно» иwait $!
(2) также отправитьSIGCLD
/SIGCHLD
... но на достаточно старых машинах последний либо не существует, либо не переносим (первый - System III / V, последний BSD и V7 не имели ни того, ни другого).$!
восходит, по крайней мере, к V7, и, безусловно, предшествует тому,sh
что знал что-то о контроле за работой (на самом деле, долгое время/bin/sh
на BSD не контролировали работу; нужно было бежать,csh
чтобы его получить - но$!
был там) ).$!
.Хотя в coretuils версии 7.0 есть команда timeout, вы упомянули некоторые среды, в которых ее нет. К счастью, на pixelbeat.org написан сценарий тайм-аута
sh
.Я использовал его раньше несколько раз, и он работает очень хорошо.
http://www.pixelbeat.org/scripts/timeout ( Примечание . Сценарий, приведенный ниже, был немного изменен по сравнению с сценарием на pixelbeat.org, см. комментарии ниже к этому ответу.)
источник
</dev/stdin
это неоперация.</dev/tty
позволит ему читать с терминала, что достаточно для моего случая использования.Как насчет (ab) использовать NC для этого
Подобно;
Или свернут в одну командную строку;
Последняя версия, хотя выглядит странно, действительно работает, когда я тестирую ее в системе linux - я думаю, что она должна работать в любой системе, и если нет, то изменение перенаправления вывода может решить проблему переносимости. выгода здесь состоит в том, что никакие фоновые процессы не вовлечены.
источник
nc
старого Unix Boxen, ни многих встроенных Linux.Другой способ запустить конвейер в своей собственной группе процессов - запустить
sh -c '....'
в псевдотерминале, используяscript
команду (которая неявно применяетsetsid
функцию).источник
script
старого Unix Boxen, ни многих встроенных Linux.Ответ в https://unix.stackexchange.com/a/18711 очень хороший.
Я хотел достичь аналогичного результата, но без необходимости явного вызова оболочки снова, потому что я хотел вызвать существующие функции оболочки.
Используя bash, можно сделать следующее:
Итак, предположим, у меня уже есть функция оболочки
f
:Выполняя это я вижу:
источник
источник