Halbasynchrones Rohr

11

Angenommen, ich habe die folgende Pipe:

a | b | c | d

Wie kann ich auf den Abschluss von c(oder b) in shoder warten bash? Dies bedeutet, dass das Skript djederzeit gestartet werden kann (und nicht gewartet werden muss), jedoch eine vollständige Ausgabe von benötigt c, um ordnungsgemäß zu funktionieren.

Der Anwendungsfall ist ein difftoolfür gitden Vergleich von Bildern. Es wird von aufgerufen gitund muss seine Eingabe (das a | b | cTeil) verarbeiten und die Ergebnisse des Vergleichs (das dTeil) anzeigen . Der Anrufer löscht die für aund erforderlichen Eingaben b. Dies bedeutet, dass der Prozess c(oder b) beendet werden muss , bevor Sie vom Skript zurückkehren . Andererseits kann ich nicht warten, dda dies bedeutet, dass ich auf Benutzereingaben warte.

Ich weiß, dass ich die Ergebnisse cin eine temporäre Datei schreiben oder vielleicht ein FIFO verwenden kann bash. (Sie sind sich jedoch nicht sicher, ob das FIFO helfen wird.) Ist dies ohne temporäre Dateien möglich sh?

BEARBEITEN

Vielleicht würde es ausreichen, wenn ich die Prozess-ID des c(oder b) Prozesses zuverlässig herausfinden könnte . Dann könnte die gesamte Pipe asynchron gestartet werden und ich könnte auf eine Prozess-ID warten. Etwas in der Art von

wait $(a | b | { c & [print PID of c] ; } | d)

EDIT ^ 2

Ich habe eine Lösung gefunden, Kommentare (oder noch bessere Lösungen) sind willkommen.

krlmlr
quelle
Sie möchten ddie cAusgabe erst nach cAbschluss der Verarbeitung starten ? Sie möchten nicht djede Ausgabezeile sofort verarbeiten?
Terdon
@terdon: Nein, kann jederzeit dbeginnen, cmuss aber beendet werden, bevor ich weitermachen kann.
krlmlr
Das scheint sich selbst zu widersprechen. Wenn Sie danfangen können, wann es Ihnen gefällt, worauf warten Sie dann genau?
Terdon
@terdon: Erweitert, um den Anwendungsfall anzuzeigen.
krlmlr
Wenn ddie Ausgabe von nicht verwendet wird c, scheint es keinen Sinn zu machen, dTeil der Pipeline zu sein. Wenn ddie Eingabe jedoch verwendet wird, dmuss eine Weile an der Eingabe gearbeitet werden, nachdem Sie alles gelesen haben, damit Ihr Ansatz einen Unterschied macht.
Hauke ​​Laging

Antworten:

6
a | b | { c; [notify];} | d

Die Benachrichtigung kann zB durch ein Signal an eine PID erfolgen, die in einer Umgebungsvariablen ( kill -USR1 $EXTPID) übergeben wurde, oder durch Erstellen einer Datei ( touch /path/to/file).

Eine andere Idee:

Sie führen den nächsten Prozess (den, auf den Sie warten können) aus der Pipeline aus:

a | b | { c; exec >&-; nextprocess;} | d

oder

a | b | { c; exec >&-; nextprocess &} | d
Hauke ​​Laging
quelle
Vielen Dank. Das Erstellen einer temporären Datei für die Zwischenausgabe ist jedoch ausführlicher und einfacher zu analysieren (für einen Menschen). Wie vermeide ich die Rennbedingung mit dem Signal? ... Ich hatte auf eine sauberere Lösung gehofft, aber wenn dies der richtige Weg ist, dann soll es so sein.
krlmlr
@krlmlr Welche Rennbedingungen?
Hauke ​​Laging
notifykönnte ausgeführt werden, bevor ich die Signalfalle einrichte. Wie kann ich sicherstellen, dass dieses Signal nicht verpasst wird?
krlmlr
@krlmlr Sie stoppen das Skript, das die Pipeline aufruft, bis es selbst ein Signal empfängt, das gesendet wird, nachdem das trapdefiniert wurde.
Hauke ​​Laging
Ihre Bearbeitung: Die Pipe wird in einer Schleife aufgerufen, über die ich keine Kontrolle habe. Leider { c; bg; }oder { c; exit 0; }scheint nicht zu funktionieren.
krlmlr
5

Wenn ich Ihre Frage richtig verstehe, sollte dies funktionieren:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(Der fd 3-Trick ist, weil einige (die meisten) Shells stdin mit / dev / null umleiten &).

Stéphane Chazelas
quelle
4

In bash können Sie eine Prozessersetzung verwenden . Der Befehl in der Prozessersetzung wird asynchron ausgeführt und nicht gewartet.

a | b | c > >(d)
Gilles 'SO - hör auf böse zu sein'
quelle
Vielen Dank. Das ist bashnur richtig - keine shUnterstützung?
krlmlr
@krlmlr Nur Bash / zsh (und ksh93 mit ein paar Modifikationen).
Gilles 'SO - hör auf böse zu sein'
3

Dies ist, was ich durch Versuch und Irrtum mit Hilfe von Haukes Input gefunden habe:

a | b | { c; kill -PIPE $$; } | d

Gleichwertig:

a | b | ( c; kill -PIPE $$; ) | d

(Letzteres ist expliziter, da {}es ohnehin in einer Unterschale ausgeführt wird, wenn es sich in einem Rohr befindet.)

Einige andere Signale (einschließlich QUIT, TERMund USR1) auch funktioniert jedoch in diesem Fall die Beschreibung des Signals wird auf dem Terminal angezeigt.

Ich frage mich, ob dies die ursprüngliche Absicht des PIPESignals ist. Nach dem Handbuch :

SIGPIPE: Das PIPESignal wird an einen Prozess gesendet, wenn versucht wird, in eine Pipe zu schreiben, ohne dass ein Prozess mit dem anderen Ende verbunden ist.

Dies bedeutet, dass wenn ich ein Pipe-Signal künstlich an die Subshell sende, es stillschweigend endet und der Endverbraucher ( d) in Ruhe lässt.

Dies funktioniert sowohl in shals auch bash.

krlmlr
quelle
0

Sie können das spongeProgramm aus dem Paket "moreutils" verwenden:

a | b | c | sponge | d

Schwamm wird für das Ende der cAusgabe vor dem Weiterleiten an d. Hoffe das ist was du wolltest.

Minijackson
quelle
0

Sie können einfach tun:

a | b | c | (d ; cat > /dev/null)

Wenn der dVorgang abgeschlossen ist, catwird der Rest der cAusgabe absorbiert, bis der Vorgang abgeschlossen ist.

In Ordnung. Nach Kommentaren denke ich, dass die Antwort darin besteht, direkt dim Hintergrund zu beginnen.

Tun:

a | b | c | (d &)

oder verwenden Sie die Lösung von Stephane Chazelas , wenn Probleme beim dLesen von stdin auftreten .

Angus
quelle
Ich fürchte, mein Anwendungsfall ist eher umgekehrt: Er cendet früher als dund ich muss warten, bis er cfertig ist, aber es ist mir egal d.
krlmlr
Entschuldigung, ich verstehe es nicht. Was möchten Sie tun, wenn der cVorgang abgeschlossen ist und dnoch ausgeführt wird? Töten d?
Angus
dhat eine GUI und kann ausgeführt werden, bis der Benutzer sie schließt.
krlmlr
OK ... aber das passiert schon. Wenn a, bund cihre Arbeit beenden, schließen sie stdout und verlassen; und dläuft nur noch. Möchten Sie nach Abschluss des Vorgangs din den Hintergrund senden c? Ist es das?
Angus
Ja, dsollte nach Abschluss in den Hintergrund gesendet werden c.
krlmlr