it-swarm.com.de

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

Ich habe ein Programm, das nützliche Informationen über stdout erzeugt, aber auch aus stdin liest. Ich möchte die Standardausgabe in eine Datei umleiten, ohne etwas für die Standardeingabe bereitzustellen. 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 ("ausgesetzt (tty input)").

Wenn ich mache:

program < /dev/null > output &

das Programm wird sofort beendet, da es EOF erreicht.

Es scheint, dass ich in program etwas pfeifen muss, das auf unbestimmte Zeit nichts tut und stdin nicht liest. 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. Dort muss eine elegante Möglichkeit sein, mit Standard-Unix-Dienstprogrammen "auf unbestimmte Zeit nichts zu tun" (um man true Zu paraphrasieren). Wie könnte ich das erreichen? (Meine Hauptkriterien für Eleganz hier: keine temporären Dateien; kein Warten oder periodisches Aufwecken; keine exotischen Dienstprogramme; so kurz wie möglich.)

87
a3nm

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

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

Das beginnt program im Hintergrund, wobei die Eingabe von einem pipe umgeleitet wird. Das andere Ende des Rohrs ist zur Schale hin offen.

Drei Vorteile dieses Ansatzes

  • kein zusätzlicher Prozess
  • sie können das Skript beenden, wenn program stirbt (warten Sie mit wait darauf).
  • program wird beendet (erhalten Sie eof auf seinem Standard, wenn die Shell beendet wird).
17

Ich glaube nicht, dass Sie eleganter werden als die

tail -f /dev/null

dass Sie bereits vorgeschlagen haben (vorausgesetzt, dies verwendet intern inotify, sollte es keine Abfragen oder Aufweckvorgänge geben, daher sollte es ausreichen, außer seltsam auszusehen).

Sie benötigen ein Dienstprogramm, das unbegrenzt ausgeführt wird, dessen Standardausgabe offen bleibt, aber nichts in Standardausgabe schreibt und nicht beendet wird, wenn seine Standardausgabe geschlossen wird. So etwas wie yes schreibt tatsächlich in stdout. cat wird beendet, wenn sein Standard geschlossen wird (oder was auch immer Sie in ihn umleiten, ist erledigt). Ich glaube sleep 1000000000d könnte funktionieren, aber das tail ist eindeutig besser. Meine Debian-Box hat ein tailf, das den Befehl leicht verkürzt.

Wie wäre es, wenn Sie das Programm unter screen ausführen?

81
P.T.

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

Sie können infinity verwenden, da sleep eine Gleitkommazahl * akzeptiert, die dezimal, hexadezimal sein kann , nendlich oder NaN gemäß man strtod.

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

50
Zaz
sleep 2147483647 | program > output &

Ja, 2^31-1 ist eine endliche Zahl, und sie läuft nicht für immer, aber ich gebe dir 1000 $, wenn der Schlaf endlich abläuft. (Hinweis: Bis dahin ist einer von uns tot.)

  • keine temporären Dateien; prüfen.
  • kein beschäftigtes Warten oder periodisches Aufwachen; prüfen
  • keine exotischen Dienstprogramme; prüfen.
  • so kurz wie möglich. Okay, es könnte kürzer sein.
19
Rob

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

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

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

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

Dadurch wird eine Shell gestartet, die sofort SIGSTOP gesendet wird, wodurch der Prozess angehalten wird. Dies wird als "Eingabe" für Ihr Programm verwendet. Das Komplement von SIGSTOP ist SIGCONT, d. H. Wenn Sie wissen, dass die Shell die PID 12345 hat, können Sie kill -CONT 12345 damit es weitergeht.

5
roaima

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 einer Pipe ist, erhalten Sie das Leseende der Pipe, also hier das gleiche wie auf dem Standard des Programms. Im Grunde wird read niemals zurückkehren, da das einzige, was in diese Pipe schreiben kann, sich selbst ist und read nichts ausgibt.

Es funktioniert auch unter FreeBSD oder Solaris, aber aus einem anderen Grund. Dort erhalten Sie durch das Öffnen von/dev/fd/1 die gleiche Ressource wie beim Öffnen von fd 1, wie Sie es erwarten würden, und wie es die meisten Systeme außer Linux tun, also das Schreibende der Pipe. Unter FreeBSD und Solaris sind Pipes jedoch bidirektional. Solange program nicht in seinen Standard schreibt (keine Anwendung), erhält read aus dieser Richtung der Pipe nichts zu lesen.

Auf Systemen, auf denen Pipes nicht bidirektional sind, schlägt read wahrscheinlich mit einem Fehler fehl, wenn versucht wird, aus einem Nur-Schreib-Dateideskriptor zu lesen. Beachten Sie auch, dass nicht alle Systeme /dev/fd/x Haben.

2

Stéphane Chazelas 'readLösung funktioniert auch unter Mac OS X, wenn unter /dev/fd/1 Ein Lese-fd geöffnet wird.

# 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/null In einem Skript (z. B. mit SIGINT) beenden zu können, müssen der Befehl tail und wait im Hintergrund angezeigt werden.

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