Jak nic nie robić wiecznie w elegancki sposób?


82

Mam program, który generuje przydatne informacje, stdoutale także czyta z stdin. Chcę przekierować standardowe wyjście do pliku, nie udostępniając niczego na standardowym wejściu. Jak dotąd tak dobrze: mogę zrobić:

program > output

i nie rób nic w tty.

Problem polega jednak na tym, że chcę to zrobić w tle. Jeśli zrobię:

program > output &

program zostanie zawieszony („zawieszony (wejście tty)”).

Jeśli zrobię:

program < /dev/null > output &

program kończy się natychmiast, ponieważ osiąga EOF.

Wydaje się, że potrzebuję połączyć się z programczymś, co nie robi niczego przez czas nieokreślony i nie czyta stdin. Działa następujące podejście:

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

Wszystko to jest jednak bardzo brzydkie. Tam ma być elegancki sposób, przy użyciu standardowych narzędzi systemu Unix, aby „nie robić nic, na czas nieokreślony” (parafrazując man true). Jak mogłem to osiągnąć? (Moje główne kryteria elegancji tutaj: brak plików tymczasowych; brak zajętych oczekiwań lub okresowych pobudek; brak egzotycznych narzędzi; możliwie najkrótszy.)


Spróbować su -c 'program | output &' user. Zaraz zadam podobne pytanie dotyczące tworzenia zadań w tle jako akceptowalnej metody obsługi „usługi / demona”. Zauważyłem również, że nie mogę przekierować STDERRbez przekierowania STDOUT. Rozwiązanie, w którym program A wysyła STDOUTdo STDINprogramu B, a następnie przekierowuje STDERRdo pliku dziennika:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc

może ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc

co to za program?
João Portela

João Portela: To jest program, który napisałem, gitorious.org/irctk
a3nm

Dlaczego nie po prostu dodać przełącznika do napisanego programu? Zakładam również, że jeśli zamkniesz stdin za pomocą 1<&-tego programu, wyjdziesz z programu?
w00t,

Odpowiedzi:


16

W powłokach, które je obsługują (ksh, zsh, bash4), możesz zacząć programjako koproces .

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

Który rozpoczyna programsię w tle z jego wejście, które zostało przekształcone od A pipe. Drugi koniec rury jest otwarty dla skorupy.

Trzy zalety tego podejścia

  • bez dodatkowego procesu
  • możesz wyjść ze skryptu, gdy programumiera (użyj, waitaby na niego poczekać)
  • programzakończy działanie (wejdzie eofna standardowe wejście, jeśli powłoka wyjdzie).

To wydaje się działać i wygląda na świetny pomysł! (Szczerze mówiąc, poprosiłem o coś do potoku do mojej komendy, a nie o funkcję powłoki, ale to był tylko problem XY w grze.) Zastanawiam się nad przyjęciem tej odpowiedzi zamiast jednej z @ PT.
a3nm

1
@ a3nm, tail -f /dev/nullnie jest idealny, ponieważ dokonuje odczytu co sekundę /dev/null(obecne wersje GNU tail w Linuksie używają tego, by wiedzieć, że w rzeczywistości jest błąd ). sleep influb jego bardziej przenośnym odpowiednikiem sleep 2147483647są lepsze podejścia do polecenia, które tam siedzi i nie robi nic IMO (uwaga sleepjest wbudowana w kilka powłok takich jak ksh93lub mksh).
Stéphane Chazelas,

78

Nie sądzę, że będziesz bardziej elegancki niż

tail -f /dev/null

które już zasugerowałeś (zakładając, że używa to wewnętrznie inotify, nie powinno być sondowania ani wybudzeń, więc poza dziwnym wyglądem powinno wystarczyć).

Potrzebujesz narzędzia, które będzie działać przez czas nieokreślony, utrzyma stdout otwarte, ale tak naprawdę nie napisze niczego na stdout i nie wyjdzie, gdy jego stdin jest zamknięte. Coś takiego yesfaktycznie pisze na standardowe wyjście. catwyjdzie, gdy jego standardowe wejście zostanie zamknięte (lub zrobione zostanie cokolwiek, na co zostaniesz przekierowany). Myślę, że sleep 1000000000dmoże działać, ale tailjest wyraźnie lepiej. Moje pudełko Debiana ma tailfnieco skrócone polecenie.

Biorąc pod uwagę inny sprzęt, co powiesz na uruchomienie programu screen?


tail -f /dev/nullNajbardziej podoba mi się to podejście i uważam je za wystarczająco eleganckie, ponieważ użycie polecenia dość ściśle odpowiada zamierzonemu celowi.
jw013,

2
Od strace tail -f /dev/nullWydaje się, że tailużywa inotifyi wybudzeń występuje w głupich takich przypadkach sudo touch /dev/null. To smutne, że wydaje się, że nie ma lepszego rozwiązania ... Zastanawiam się, który byłby odpowiedni system, aby użyć lepszego rozwiązania.
a3nm

5
@ a3nm Syscall byłby pause, ale nie jest narażony bezpośrednio na interfejs powłoki.
Gilles

PT: Wiem o tym screen, ale ma to na celu uruchomienie wielu wystąpień programu ze skryptu powłoki do celów testowych, więc używanie screenjest nieco przesadne.
a3nm

3
@sillyMunky Silly Monkey, odpowiedź WaelJa jest zła (wysyła nieskończone zera na standardowe wejście).
PT

48

sleep infinity to najczystsze znane mi rozwiązanie.

Możesz użyć, infinityponieważ sleepakceptuje liczbę zmiennoprzecinkową * , która może być dziesiętna , szesnastkowa , nieskończoności lub NaN , zgodnie z man strtod.

* To nie jest część standardu POSIX, więc nie jest tak przenośna jak tail -f /dev/null. Jest jednak obsługiwany w jądrach GNU (Linux) i BSD (używany na Macu) (najwyraźniej nie jest obsługiwany w nowszych wersjach Maca - patrz komentarze).


Haha, to naprawdę miłe podejście. :)
a3nm

@ a3nm: Dzięki:) Wydaje się, że działasleep infinity również na BSD i Mac .
Zaz

Jakie zasoby wymaga proces nieskończonego spania? Po prostu RAM?
CMCDragonkai

1
Ta odpowiedź twierdzi, że sleep infinityczeka maksymalnie 24 dni; kto ma rację?
nh2

1
@Zaz Dokładnie zbadałem teraz ten problem. Okazuje się, że początkowo miałeś rację! sleepNarzędzie jest nie ograniczone do 24 dni ; jest to tylko pierwszy system, który śpi przez 24 dni, a potem wykona więcej takich połączeń. Zobacz mój komentarz tutaj: stackoverflow.com/questions/2935183/…
nh2

19
sleep 2147483647 | program > output &

Tak, 2^31-1jest liczbą skończoną i nie będzie trwać wiecznie , ale dam ci 1000 $, kiedy sen wreszcie się skończy. (Wskazówka: do tego czasu jedno z nas nie żyje).

  • brak plików tymczasowych; czek.
  • bez czekania lub okresowych wybudzeń; czek
  • bez egzotycznych narzędzi; czek.
  • tak krótko, jak to możliwe. Okej, może być krótszy.

5
bash: spać $ ((64 # 1 _____)) | program> wyjście i
Diego Torres Milano

Śpi przez 68 lat , śpi przez 98 wieków : sleep 2147483647d...
agc

9

Możesz utworzyć plik binarny, który robi to samo za pomocą:

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

5

Oto kolejna sugestia użycia standardowych narzędzi uniksowych, aby „nic nie robić w nieskończoność” .

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

Spowoduje to uruchomienie powłoki, która jest natychmiast wysyłana SIGSTOP, co powoduje zawieszenie procesu. Jest to używane jako „dane wejściowe” do programu. Uzupełnieniem SIGSTOPjest SIGCONT, tzn. Jeśli wiesz, że powłoka ma PID 12345, możesz kill -CONT 12345ją kontynuować.


2

W systemie Linux możesz wykonać:

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

W Linuksie, otwarcie / dev / fd / x, gdzie x jest deskryptorem pliku do końca zapisu potoku, daje ci koniec odczytu potoku, więc tutaj to samo, co na standardowym programie. Zasadniczo więc readnigdy nie powróci, ponieważ jedyną rzeczą, która może napisać do tego potoku, jest sama i readniczego nie wyświetla.

Będzie również działał na FreeBSD lub Solaris, ale z innego powodu. Tam otwarcie / dev / fd / 1 daje ci taki sam zasób jak open na fd 1, tak jak można się spodziewać i jak większość systemów z wyjątkiem Linuksa, więc koniec pisania potoku. Jednak w FreeBSD i Solaris potoki są dwukierunkowe. Tak długo, jak programnie pisze do standardowego wejścia (żadna aplikacja tego nie robi), nie readbędzie nic czytać z tego kierunku potoku.

W systemach, w których potoki nie są dwukierunkowe, readprawdopodobnie wystąpi błąd przy próbie odczytu z deskryptora pliku tylko do zapisu. Należy również pamiętać, że nie wszystkie systemy mają /dev/fd/x.


Bardzo dobrze! W rzeczywistości moje testy nie wymagają xbash; dalej z zsh możesz po prostu zrobić readi to działa (choć nie rozumiem dlaczego!). Czy ta sztuczka jest specyficzna dla Linuksa, czy działa na wszystkich systemach * nix?
a3nm

@ a3nm, jeśli zrobisz to readsam, odczyta ze standardowego wejścia. Więc jeśli jest to terminal, będzie czytał to, co piszesz, dopóki nie naciśniesz enter.
Stéphane Chazelas,

jasne, rozumiem, co robi czytanie. Nie rozumiem, dlaczego czytanie z terminala przy pomocy odczytu w procesie w tle blokuje bash, ale nie zsh.
a3nm

@ a3nm, nie jestem pewien, co masz na myśli. Co masz na myśli mówiąc, że możesz po prostu zrobić readi to działa ?
Stéphane Chazelas,

Mówię, że z zsh możesz po prostu zrobić read | program > outputi działa tak samo, jak zasugerowałeś. (I nie rozumiem dlaczego.)
a3nm

0

Stephane Chazelas' read rozwiązanie działa na Mac OS X, a także czy fd odczyt zostanie otwarty /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

Aby móc zabijać tail -f /dev/nullw skrypcie (na przykład za pomocą SIGINT), konieczne jest tło tailpolecenia i wait.

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

-2

Przekieruj /dev/zerojako standardowe wejście!

program < /dev/zero > output &

9
To dałoby jego programowi nieskończoną liczbę zerowych bajtów ... co niestety sprawiłoby, że byłaby zajęta.
Jander

1
To nie jest prawda jander, / dev / zero nigdy się nie zamknie, trzymając łańcuch rurowy otwarty. Jednak plakat mówi, że nie przyjmuje standardu, więc żadne zera nigdy nie zostaną przeniesione do programu. To wcale nie jest zajęta pętla, to tylko czekanie.
sillyMunky

2
przepraszam, OP używa standardowego wejścia, więc to wyczyści jego dane wejściowe i będzie rysował z / dev / zero. Następnym razem powinienem przeczytać dwa razy! Gdyby OP nie używał standardowego wejścia, byłoby to najbardziej eleganckie rozwiązanie, jakie widziałem, i nie wymagałoby to dużego wysiłku.
sillyMunky
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.