Wie kann man auf elegante Weise nichts für immer tun?

82

Ich habe ein Programm, das nützliche Informationen erzeugt, stdoutaber auch ausliest stdin. Ich möchte die Standardausgabe in eine Datei umleiten, ohne die Standardeingabe zu ändern. So weit, so gut: Ich kann:

program > output

und mach nichts in der tty.

Das Problem ist jedoch, dass ich dies im Hintergrund tun möchte. Wenn ich mache:

program > output &

Das Programm wird angehalten ("angehalten (tty input)").

Wenn ich mache:

program < /dev/null > output &

Das Programm wird sofort beendet, da es EOF erreicht.

Es scheint, dass ich in programetwas hineinpfeifen muss, das auf unbestimmte Zeit nichts tut und nicht liest stdin. Die folgenden Ansätze funktionieren:

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

Dies ist jedoch alles sehr hässlich. Es muss eine elegante Möglichkeit geben, mit Standard-Unix-Dienstprogrammen "nichts auf unbestimmte Zeit zu tun" (um es zu paraphrasieren man true). Wie könnte ich das erreichen? (Meine Hauptkriterien für Eleganz hier: keine temporären Dateien; kein Warten oder periodisches Aufwecken; keine exotischen Hilfsprogramme; so kurz wie möglich.)

a3nm
quelle
Versuchen Sie es su -c 'program | output &' user. Ich stelle eine ähnliche Frage zum Erstellen von Hintergrundjobs als akzeptable Methode für die Behandlung eines "Service / Daemons". Ich bemerkte auch, dass ich nicht umleiten konnte, STDERRohne auch umzuleiten STDOUT. Die Lösung in dem Programa sendet STDOUTzu STDINder programB, leitet dann STDERRin eine Protokolldatei:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc
vielleicht ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc
Was für ein Programm ist das?
João Portela
João Portela: Dies ist ein Programm, das ich geschrieben habe, gitorious.org/irctk
a3nm
Warum nicht einfach einen Schalter zu dem Programm hinzufügen, das Sie geschrieben haben? Außerdem gehe ich davon aus, dass, wenn Sie stdin 1<&-damit schließen, Ihr Programm beendet wird?
Freitag,

Antworten:

16

In Shells, die sie unterstützen (ksh, zsh, bash4), können Sie programals Co-Prozess starten .

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

Das beginnt programim Hintergrund, wenn die Eingabe von a umgeleitet wird pipe. Das andere Ende des Rohrs ist zur Hülle hin offen.

Drei Vorteile dieses Ansatzes

  • Kein zusätzlicher Prozess
  • Sie können das Skript beenden, wenn programstirbt (verwenden wait, um darauf zu warten)
  • programwird beendet (wird eofauf die Standardeinstellung gesetzt, wenn die Shell beendet wird).
Stéphane Chazelas
quelle
Das scheint zu funktionieren und sieht nach einer großartigen Idee aus! (Um fair zu sein, ich hatte um etwas gebeten, das ich an meinen Befehl weiterleiten konnte, nicht um ein Shell-Feature, aber dies war nur das XY-Problem.) Ich überlege, diese Antwort zu akzeptieren, anstatt die Antwort von @ PT.
a3nm
1
@ a3nm, tail -f /dev/nullist nicht ideal, da es jede Sekunde gelesen wird /dev/null(aktuelle Versionen von GNU tail unter Linux mit inotify gibt es tatsächlich einen Fehler ). sleep infoder sein portableres Äquivalent sleep 2147483647sind bessere Ansätze für einen Befehl, der dort sitzt und nichts tut, IMO (beachten Sie, dass sleepeinige Shells wie ksh93oder eingebaut sind mksh).
Stéphane Chazelas
78

Ich glaube nicht, dass Sie eleganter werden als die

tail -f /dev/null

Das haben Sie bereits vorgeschlagen (vorausgesetzt, dies verwendet inotify intern, es sollte kein Polling oder Aufwecken geben, es sollte also ausreichend sein, außer dass es seltsam aussieht).

Sie benötigen ein Dienstprogramm, das auf unbestimmte Zeit ausgeführt wird, dessen Standardausgabe offen bleibt, das jedoch nichts in die Standardausgabe schreibt und das nicht beendet wird, wenn die Standardausgabe geschlossen wird. Sowas yesschreibt eigentlich stdout an. catwird beendet, wenn die Standardeingabe geschlossen ist (oder was auch immer Sie umleiten). Ich denke sleep 1000000000dkönnte funktionieren, aber das tailist eindeutig besser. Meine Debian-Box hat einen tailfBefehl, der sich geringfügig verkürzt.

Wie wäre es mit einem anderen Ansatz, wenn Sie das Programm unter laufen lassen screen?

PT
quelle
Ich mag den tail -f /dev/nullAnsatz am liebsten und finde ihn auch elegant genug, da die Befehlsverwendung dem beabsichtigten Zweck ziemlich genau entspricht.
JW013
2
Daraus ergibt sich, strace tail -f /dev/nulldass tailVerwendungen inotifyund das Aufwachen in albernen Fällen wie auftreten sudo touch /dev/null. Es ist traurig, dass es anscheinend keine bessere Lösung gibt ... Ich frage mich, welches der richtige Systemanruf wäre, um eine bessere Lösung zu implementieren.
a3nm
5
@ a3nm Das syscall wäre pause, aber es ist nicht direkt einer Shell-Schnittstelle ausgesetzt.
Gilles
PT: Ich weiß screen, aber dies ist zu Testzwecken, um mehrere Vorkommen des Programms von einem Shell-Skript auszuführen, also ist die Verwendung screenein bisschen übertrieben.
a3nm 20.07.12
3
@sillyMunky Silly Monkey, WaelJs Antwort ist falsch (sendet unendliche Nullen an stdin).
PT
48

sleep infinity ist die klarste Lösung, die ich kenne.

Sie können verwenden, infinityweil sleepeine Gleitkommazahl * akzeptiert wird , die je nach dem dezimal , hexadezimal , unendlich oder naN sein kann man strtod.

* Dies ist nicht Teil des POSIX-Standards, ist also nicht so portabel wie tail -f /dev/null. Es wird jedoch in GNU coreutils (Linux) und BSD (verwendet auf Mac) unterstützt (anscheinend nicht unterstützt auf neueren Versionen von Mac - siehe Kommentare).

Zaz
quelle
Haha, das ist wirklich ein schöner Ansatz. :)
a3nm
@ a3nm: Danke :) Scheint sleep infinityauch auf BSD und Mac zu funktionieren .
Zaz
Welche Art von Ressourcen verbraucht ein unendlich schlafender Prozess? Nur RAM?
CMCDragonkai
1
Diese Antwort gibt an , dass sleep infinitymaximal 24 Tage gewartet wird. wer hat recht
nh2
1
@Zaz Ich habe das Problem jetzt im Detail untersucht. Es stellt sich heraus, dass Sie anfangs richtig waren! Das sleepDienstprogramm ist nicht auf 24 Tage beschränkt . Es ist nur der erste Systemanruf, der 24 Tage lang inaktiv ist, und danach werden weitere solche Systemanrufe ausgeführt. Siehe meinen Kommentar hier: stackoverflow.com/questions/2935183/…
nh2
19
sleep 2147483647 | program > output &

Ja, es 2^31-1ist eine endliche Zahl, und sie wird nicht für immer laufen , aber ich gebe Ihnen 1000 US-Dollar, wenn der Schlaf endlich aufhört. (Hinweis: bis dahin ist einer von uns tot.)

  • keine temporären Dateien; prüfen.
  • Keine Wartezeiten oder regelmäßigen Aufwachvorgänge. prüfen
  • keine exotischen Dienstprogramme; prüfen.
  • so kurz wie möglich. Okay, es könnte kürzer sein.
rauben
quelle
5
Bash: Sleep $ ((64 # 1 _____)) | Programm> Ausgabe &
Diego Torres Milano
Das schläft seit 68 Jahren , das schläft seit 98 Jahrhunderten : sleep 2147483647d...
agc
9

Sie können eine Binärdatei erstellen, die genau das tut mit:

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

Hier ist ein weiterer Vorschlag für die Verwendung von Standard-Unix-Dienstprogrammen, um "auf unbestimmte Zeit nichts zu tun" .

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

Dies löst eine Shell aus, die sofort gesendet wird SIGSTOPund den Prozess anhält. Dies wird als "Eingabe" für Ihr Programm verwendet. Das Komplement von SIGSTOPist SIGCONT, dh wenn Sie wissen, dass die Shell die PID 12345 hat, können Sie kill -CONT 12345diese fortsetzen.

Roaima
quelle
2

Unter Linux können Sie Folgendes tun:

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

Wenn Sie unter Linux / dev / fd / x öffnen, wobei x ein Dateideskriptor für das schreibende Ende einer Pipe ist, erhalten Sie das lesende Ende der Pipe. readWird also im Grunde nie zurückkehren, weil das einzige, was in diese Pipe geschrieben werden kann, sich selbst ist und readnichts ausgibt.

Es wird auch unter FreeBSD oder Solaris funktionieren, aber aus einem anderen Grund. Wenn Sie dort / dev / fd / 1 öffnen, erhalten Sie die gleiche Ressource wie auf fd 1, die Sie erwartet haben, und wie es die meisten Systeme außer Linux tun, also das Ende der Pipe. Unter FreeBSD und Solaris sind Pipes jedoch bidirektional. Solange programalso nicht in das Standardverzeichnis geschrieben wird (keine Anwendung), readkann aus dieser Richtung der Pipe nichts gelesen werden.

Auf Systemen, auf denen Pipes nicht bidirektional sind, tritt readwahrscheinlich ein Fehler auf, wenn versucht wird, aus einem Nur-Schreib-Dateideskriptor zu lesen. Beachten Sie auch, dass nicht alle Systeme haben /dev/fd/x.

Stéphane Chazelas
quelle
Sehr schön! Tatsächlich brauchst du meine Tests nicht xmit bash; weiter mit zsh kannst du es einfach machen readund es funktioniert (obwohl ich nicht verstehe warum!). Ist dieser Trick Linux-spezifisch oder funktioniert er auf allen * nix-Systemen?
a3nm
@ a3nm, wenn du das readalleine machst , liest es von stdin. Wenn es sich also um das Terminal handelt, wird Ihre Eingabe gelesen, bis Sie die Eingabetaste drücken.
Stéphane Chazelas
Klar, ich verstehe, was gelesen wird. Was ich nicht verstehe, ist, warum das Lesen vom Terminal mit read in einem Hintergrundprozess mit bash blockiert wird, aber nicht mit zsh.
a3nm
@ a3nm, ich bin mir nicht sicher, was du meinst. Was meinst du damit, dass du einfach machen kannst readund es funktioniert ?
Stéphane Chazelas
Ich sage, dass Sie mit zsh genau das tun können read | program > output, was Sie vorgeschlagen haben. (Und ich verstehe nicht warum.)
23.08.15 Uhr
0

Die read Lösung von Stéphane Chazelas funktioniert auch unter Mac OS X, wenn eine Lese-FD geöffnet wird /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

Um tail -f /dev/nullin einem Skript töten zu können (z. B. mit SIGINT), müssen Sie den tailBefehl und im Hintergrund ausführen wait.

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

Weiterleitung /dev/zeroals Standardeingabe!

program < /dev/zero > output &
WaelJ
quelle
9
Dies würde seinem Programm eine unendliche Anzahl von Null-Bytes geben ... was es leider zu einer Besetztschleife machen würde.
Jander
1
Dies ist kein echter Jander, / dev / zero wird sich niemals schließen und die Rohrkette offen halten. Das Plakat sagt jedoch, dass er kein stdin aufnimmt, so dass niemals Nullen in das Programm übertragen werden. Dies ist überhaupt keine belegte Schleife, es ist ein reines Warten.
SillyMunky
2
Entschuldigung, OP verwendet stdin, dies löscht seine Eingabe und zeichnet von / dev / zero. Ich sollte das nächste Mal zweimal lesen! Wenn OP nicht stdin verwenden würde, wäre dies die eleganteste Lösung, die ich je gesehen habe, und es wäre kein arbeitsreiches Warten.
SillyMunky