Как ничего не делать вечно элегантным способом?

82

У меня есть программа, которая производит полезную информацию, stdoutно также читает из stdin. Я хочу перенаправить его стандартный вывод в файл, не предоставляя ничего для стандартного ввода. Пока все хорошо, я могу сделать:

program > output

и не делай ничего в tty.

Однако проблема в том, что я хочу сделать это в фоновом режиме. Если я сделаю:

program > output &

программа будет приостановлена ​​(«приостановлено (tty input)»).

Если я сделаю:

program < /dev/null > output &

программа немедленно завершается, потому что она достигает EOF.

Кажется, что мне нужно подключиться к programчему-то, что ничего не делает в течение неопределенного времени и не читает stdin. Работают следующие подходы:

while true; do sleep 100; done | program > output &
mkfifo fifo && cat fifo | program > output &
tail -f /dev/null | program > output &

Однако все это очень некрасиво. Там есть быть элегантным способом, с помощью стандартного утилита Unix, не «ничего не делать, до бесконечности» (перефразируя man true). Как я мог этого добиться? (Мои главные критерии элегантности здесь: никаких временных файлов; никаких занятых или периодических пробуждений; никаких экзотических утилит; как можно короче.)

a3nm
источник
Попробуй su -c 'program | output &' user. Я собираюсь задать аналогичный вопрос с созданием фоновых заданий в качестве приемлемого метода для обработки «service / daemon». Я также заметил, что я не мог перенаправить STDERRбез перенаправления STDOUT. Решение, в котором программа A отправляет STDOUTв STDINпрограмму B, а затем перенаправляет STDERRв файл журнала:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc
может быть ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc
что это за программа?
Жоао Портела
Жоао Портела: Это программа, которую я написал, gitorious.org/irctk
a3nm,
Почему бы просто не добавить переключатель в ту программу, которую вы написали? Кроме того, я предполагаю, что если вы закроете стандартный ввод с 1<&-ним, выйдет из вашей программы?
19:00

Ответы:

16

В оболочках, которые их поддерживают (ksh, zsh, bash4), вы можете начать programкак совместный процесс .

  • ksh: program > output |&
  • zsh, bash:coproc program > output

Это начинается programв фоновом режиме с ввода, перенаправленного из pipe. Другой конец трубы открыт для оболочки.

Три преимущества такого подхода

  • без дополнительного процесса
  • вы можете выйти из скрипта, когда programумрете (используйте, waitчтобы ждать его)
  • programзавершится (войдите eofв свой стандартный ввод, если оболочка выйдет).
Стефан Шазелас
источник
Это похоже на работу и выглядит как отличная идея! (Если честно, я попросил что-то передать в мою команду, а не функцию оболочки, но это была только проблема XY в игре.) Я подумываю принять этот ответ вместо @ PT.
a3nm
1
@ a3nm, tail -f /dev/nullне идеален, так как выполняет чтение каждую секунду /dev/null(в текущих версиях GNU tail для Linux, использующих inotify, на самом деле есть ошибка ). sleep infили его более переносимый эквивалент sleep 2147483647- лучшие подходы для команды, которая сидит там и ничего не делает IMO (обратите внимание, что sleepона встроена в несколько оболочек типа ksh93or mksh).
Стефан Шазелас
78

Я не думаю, что ты станешь более элегантным, чем

tail -f /dev/null

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

Вам нужна утилита, которая будет работать неопределенно долго, будет держать свой стандартный вывод открытым, но на самом деле ничего не будет записывать в стандартный вывод и не будет завершать работу, когда ее стандартный вывод закрыт. Нечто подобное на yesсамом деле пишет в стандартный вывод. catвыйдет, когда его стандартный ввод будет закрыт (или все, что вы перенаправили в него, будет сделано). Я думаю, что sleep 1000000000dможет работать, но tailявно лучше. В моей коробке Debian есть tailfкоманда, которая немного укорачивает команду.

Взять другой курс, как насчет запуска программы под screen?

PT
источник
Мне нравится этот tail -f /dev/nullподход лучше всего, и я нахожу его достаточно элегантным, поскольку использование команды довольно точно соответствует назначению.
jw013
2
Из strace tail -f /dev/nullтого, что кажется, что tailиспользует inotifyи что пробуждения происходят в глупых случаях, как sudo touch /dev/null. Грустно, что, кажется, нет лучшего решения ... Интересно, какой правильный системный вызов использовать для реализации лучшего решения.
a3nm
5
@ a3nm Системный вызов может быть pause, но он не напрямую подключен к интерфейсу оболочки.
Жиль
PT: Я знаю об этом screen, но это для запуска нескольких вхождений программы из сценария оболочки в целях тестирования, поэтому использовать screenэто немного излишне.
a3nm
3
@sillyMunky Глупая Обезьяна, ответ WaelJ неправильный (отправляет бесконечные нули в stdin).
PT
48

sleep infinity это самое ясное решение, которое я знаю.

Вы можете использовать, infinityпотому что sleepпринимает число с плавающей запятой * , которое может быть десятичным , шестнадцатеричным , бесконечностью или NaN , в соответствии с man strtod.

* Это не является частью стандарта POSIX, поэтому не так переносимо, как tail -f /dev/null. Однако он поддерживается в GNU coreutils (Linux) и BSD (используется на Mac) (очевидно, не поддерживается в более новых версиях Mac - см. Комментарии).

Zaz
источник
Ха-ха, это действительно хороший подход. :)
a3nm
@ a3nm: Спасибо :) Кажется, sleep infinityтакже работает на BSD и Mac .
Заз
Какие ресурсы берет бесконечно спящий процесс? Просто оперативка?
CMCDragonkai
1
Этот ответ утверждает, что sleep infinityждет в течение 24 дней максимум; кто прав?
nh2
1
@Zaz Я подробно изучил проблему сейчас. Оказывается, вы изначально были правы! sleepУтилита не ограничивается до 24 дней ; это всего лишь первый системный вызов, который спит в течение 24 дней, и после этого он будет делать больше таких системных вызовов. Смотрите мой комментарий здесь: stackoverflow.com/questions/2935183/…
nh2
19
sleep 2147483647 | program > output &

Да, 2^31-1это конечное число, и оно не будет работать вечно , но я дам вам 1000 долларов, когда время сна наконец истечет. (Подсказка: один из нас к тому времени умрет.)

  • нет временных файлов; проверить.
  • нет занятых ожиданий или периодических пробуждений; проверить
  • никаких экзотических утилит; проверить.
  • как можно короче. Хорошо, это может быть короче.
обкрадывать
источник
5
bash: sleep $ ((64 # 1 _____)) | программа> выход &
Диего Торрес Милано
Это спит в течение 68 лет , это спит в течение 98 века : sleep 2147483647d...
AGc
9

Вы можете создать двоичный файл, который делает это с:

$ echo 'int main(){ pause(); }' > pause.c; make pause
PSkocik
источник
5

Вот еще одно предложение использовать стандартные утилиты Unix, чтобы «ничего не делать, бесконечно» .

sh -c 'kill -STOP $$' | program > output

Это запускает оболочку, которая немедленно отправляется SIGSTOP, что приостанавливает процесс. Это используется как «вход» в вашу программу. Дополнением SIGSTOPявляется SIGCONT, т.е. если вы знаете, что оболочка имеет PID 12345, вы можете kill -CONT 12345сделать так, чтобы она продолжалась.

roaima
источник
2

В Linux вы можете сделать:

read x < /dev/fd/1 | program > output

В Linux, открыв / dev / fd / x, где x - дескриптор файла для конца записи канала, вы получите конец чтения канала, так что здесь то же самое, что и в stdin программы. Так что, по сути, readникогда не вернется, потому что единственное, что может записать в этот канал, это само по себе, и readничего не выводит.

Он также будет работать на FreeBSD или Solaris, но по другой причине. Там, открыв / dev / fd / 1, вы получите тот же ресурс, что и open на fd 1, как и следовало ожидать, и как и большинство систем, кроме Linux, так что конец написания канала. Однако в FreeBSD и Solaris каналы являются двунаправленными. Поэтому до тех пор, пока programон не пишет в свой стандартный ввод (ни одно приложение не делает этого), он readне получит ничего, чтобы читать с этого направления канала.

В системах, где каналы не являются двунаправленными, readвероятно , произойдет сбой с ошибкой при попытке чтения из дескриптора файла только для записи. Также обратите внимание, что не все системы имеют /dev/fd/x.

Стефан Шазелас
источник
Очень хорошо! На самом деле мои тесты вам не нужны xс Bash; далее с Zsh вы можете просто сделать, readи это работает (хотя я не понимаю, почему!). Это трюк, специфичный для Linux, или он работает на всех * nix системах?
a3nm
@ a3nm, если ты будешь readодин, он будет читать со стандартного ввода. Так что, если это терминал, он будет читать то, что вы вводите, пока вы не нажмете ввод.
Стефан Шазелас
конечно, я понимаю, что читает. Чего я не понимаю, так это того, почему чтение из терминала с чтением в фоновом процессе блокируется с помощью bash, а не с помощью zsh.
a3nm
@ a3nm, я не уверен, что ты имеешь в виду. Что ты имеешь в виду под тем, что ты можешь сделать, readи это работает ?
Стефан Шазелас
Я говорю, что с Zsh вы можете просто сделать, read | program > outputи это работает так же, как вы предложили. (И я не понимаю, почему.)
a3nm
0

read Решение Стефана Чазела работает и на Mac OS X, если открыто чтение с fd /dev/fd/1.

# using bash on Mac OS X
# -bash: /dev/fd/1: Permission denied
read x </dev/fd/1 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  1 0

exec 3<&- 3</dev/fd/1
read x 0<&3 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  0 0

Чтобы иметь возможность убивать tail -f /dev/nullв скрипте (например, с помощью SIGINT), необходимо выполнить tailкоманду background и wait.

#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!
user6915
источник
-2

Перенаправить /dev/zeroкак стандартный ввод!

program < /dev/zero > output &
WaelJ
источник
9
Это дало бы его программе бесконечное число нулевых байтов ... что, к сожалению, сделало бы цикл занятым.
Джандер
1
Это не настоящий jander, / dev / zero никогда не закроется, удерживая цепь трубы открытой. Однако, по словам автора, он не принимает стандартный ввод, поэтому нули никогда не будут переданы в программу. Это не занятый цикл, это просто ожидание.
sillyMunky
2
извините, OP использует stdin, так что это уничтожит его ввод и будет рисовать из / dev / zero. Я должен прочитать дважды в следующий раз! Если бы OP не использовал stdin, это было бы самым элегантным решением, которое я видел, и не было бы занятым ожиданием.
sillyMunky