Як зробити вічно нічого не елегантно?


82

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

program > output

і нічого не робити в tty.

Однак проблема полягає в тому, що я хочу це зробити у фоновому режимі. Якщо я:

program > output &

програма буде призупинена ("призупинено (вхід tty)").

Якщо я:

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). Як я міг цього досягти? (Мої основні критерії для елегантності тут: відсутні тимчасові файли; відсутність зайнятості або періодичні пробудження; відсутні екзотичні утиліти; як можна коротше.)


Спробуйте su -c 'program | output &' user. Я збираюся задати аналогічне запитання зі створенням фонових завдань як прийнятного методу обробки "послуги / демон". Я також зауважив, що не можу переадресувати, STDERRне переспрямувавшись STDOUT. Рішення, де programA надсилає STDOUTдо STDINprogramB, потім перенаправляє 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

Чому б просто не додати комутатор до тієї програми, яку ви написали? Крім того, я припускаю, що якщо ви закриєте stdin з 1<&-ним, вийде з програми?
w00t

Відповіді:


16

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

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

Це починається programу фоновому режимі, його вхід переспрямований з а pipe. Інший кінець труби відкритий для оболонки.

Три переваги цього підходу

  • немає зайвого процесу
  • ви можете вийти зі сценарію, коли він programпомре (використовуйте, waitщоб чекати його)
  • programзакінчується (потрапляйте eofна його stdin, якщо оболонка виходить).

Це, здається, працює і виглядає як чудова ідея! (Справедливо кажучи, я попросив щось передати моїй команді, а не функції оболонки, але це була лише проблема XY в процесі гри.) Я розглядаю можливість прийняти цю відповідь замість однієї @ PT.
a3nm

1
@ a3nm, tail -f /dev/nullне є ідеальним, оскільки він читає щосекунди /dev/null(у поточних версіях хвоста GNU на Linux із використанням inotify насправді є помилка ). sleep infабо його більш портативний еквівалент sleep 2147483647- кращі підходи для команди, яка сидить там, не роблячи нічого ІМО (зауважте, що sleepвона вбудована в кілька оболонок, як ksh93або mksh).
Стефан Шазелас

78

Я не думаю, що ти вийдеш більш елегантним, ніж

tail -f /dev/null

що ви вже запропонували (якщо припустити, що це використовує внутрішню ініціативу, не повинно бути жодних опитувань чи пробуджень, тому, крім дивного вигляду, цього має бути достатньо).

Вам потрібна утиліта, яка працюватиме нескінченно, триматиме її stdout відкритою, але насправді нічого не напише в stdout і не вийде, коли її stdin закритий. Щось подібне yesнасправді пише до stdout. catвийде, коли його stdin закритий (або все, що ви повторно направляєте в нього, робиться). Я думаю, що sleep 1000000000dможе спрацювати, але tailце явно краще. Моє поле Debian має tailfкоманду, яка трохи скорочує.

Взявши інший крок, як щодо запуску програми під screen?


Мені найбільше подобається tail -f /dev/nullпідхід і вважаю його досить елегантним, оскільки використання команд досить точно відповідає наміченому призначенню.
jw013

2
З strace tail -f /dev/nullнього , здається , що tailвикористовує inotifyі пробудження відбувається в дурних подібних випадках sudo touch /dev/null. Сумно, що, здається, немає кращого рішення ... Цікаво, який би правильний syscall використати для реалізації кращого рішення.
a3nm

5
@ a3nm Syscall був би 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 - див. Коментарі).


Ха-ха, це дійсно приємний підхід. :)
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: сон $ ((64 # 1 _____)) | програма> вихід &
Дієго Торрес Мілано

Що спить 68 років , це спить 98 століть : sleep 2147483647d...
agc

9

Ви можете створити двійковий файл, який робить це саме з:

$ echo 'int main(){ pause(); }' > pause.c; make pause

5

Ось ще одна пропозиція, що використовує стандартні утиліти Unix, щоб "робити нічого, нескінченно" .

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

При цьому негайно надсилається оболонка SIGSTOP, яка призупиняє процес. Це використовується як "вхід" у вашу програму. Доповненням SIGSTOPє SIGCONT, тобто якщо ви знаєте, що оболонка має PID 12345, ви можете kill -CONT 12345зробити її продовження.


2

У Linux ви можете:

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

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

Він також працюватиме на FreeBSD або Solaris, але з іншої причини. Там відкриття / dev / fd / 1 отримує той самий ресурс, що і відкритий на fd 1, як ви очікували, як і більшість систем, окрім Linux, так що закінчення запису закінчується. Однак у FreeBSD та Solaris труби мають двосторонній характер. Тож, поки programне запише до свого stdin (жодна програма не робить), не readбуде нічого читати з цього напрямку труби.

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


Дуже хороша! Насправді мої тести вам не потрібні xз басом; далі з zsh ви можете просто робити, readі це працює (хоча я не розумію, чому!). Це хитрість, специфічна для Linux, чи вона працює у всіх * nix системах?
a3nm

@ a3nm, якщо ви зробите readодин, він буде читати з stdin. Отже, якщо це термінал, він буде читати те, що ви вводите, доки не натиснете Enter.
Стефан Шазелас

звичайно, я розумію, що читає. Що я не розумію, це те, чому читання з терміналу з читанням у фоновому процесі блокується bash, але не zsh.
a3nm

@ a3nm, я не впевнений, що ти маєш на увазі. Що ти маєш на увазі, що ти можеш просто зробити, readі це працює ?
Стефан Шазелас

Я кажу, що з zsh ви можете просто робити, read | program > outputі він працює так само, як ви запропонували. (І я не розумію чому.)
a3nm

0

Stéphane Chazelas ' 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команду та wait.

#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!

-2

Перенаправити /dev/zeroяк стандартний вхід!

program < /dev/zero > output &

9
Це дало б його програмі нескінченну кількість нульових байтів ... що, на жаль, зробить це зайнятим циклом.
Джандер

1
Це не справжній яндер, / dev / zero ніколи не закриється, тримаючи ланцюг труб відкритою. Однак плакат говорить, що він не приймає stdin, тому жодні нулі ніколи не будуть передані програмі. Це зовсім не зайнятий цикл, це чисте очікування.
sillyMunky

2
Вибачте, ОП використовує stdin, тому це знищить його вхід і буде малювати з / dev / zero. Я повинен прочитати двічі наступного разу! Якби ОП не використовував stdin, це було б найелегантнішим рішенням, яке я бачив, і не було б зайнятого очікування.
sillyMunky
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.