Login
Login-Name Passwort


 
Newsletter
Werbung

Do, 23. Februar 2017, 15:00

Erst du, dann du, dann du, dann du

Synchronisation von Jobs in der Shell

Dieser Artikel geht auf einen spezifischen Aspekt ein, bei dem sich Pipes bewährt haben: die Synchronisation von Prozessen.

Vor einiger Zeit kam ein Kollege auf mich zu und fragte, wie er am besten mehrere Backup-Jobs gleichzeitig starten könne. Außerdem sollten ein paar andere Jobs später laufen, wenn die ersten fertig sind. Das Ganze wollte er möglichst schnell und ohne zusätzliche Programme erledigen - also in der Shell.

Mehrere Jobs in der Shell

Mathias Weidner

Mehrere Jobs in der Shell

Die Abbildung veranschaulicht das Problem: Zum Zeitpunkt t0 starten die ersten Jobs. Sie sind zum Zeitpunkt t1 fertig. Die nächsten Jobs sollen zum Zeitpunkt t2 starten. Eine dritte Staffel sollte frühestens zum Zeitpunkt t3 starten, wenn alle Jobs der zweiten Staffel fertig sind. Außerdem sollen t1 und t2 nicht allzu weit auseinander liegen - es ging ja um Backup-Jobs, die über Nacht laufen und am Morgen fertig sein sollen.

Die erste Idee, die Jobs mit & in den Hintergrund zu schicken, bringt einige Probleme mit sich:

  • Bei Hintergrund-Jobs bekommt ein Shell-Skript nicht mit, wann sie fertig sind. Man muss t1 und t3 daher schätzen und verschenkt Zeit, weil zuviel Reserve bleibt oder die nächste Staffel zu früh startet, während die vorherige noch läuft.
  • Die Ausgaben der einzelnen Jobs vermischen sich. Man kann nicht vorhersagen, wessen Ausgabe an welcher Stelle erscheint.
  • Man bekommt keine Rückgabewerte der Jobs und kann somit im Skript nicht darauf reagieren.

Für diese Probleme gibt es Abhilfen, auch mit den Mitteln der Shell selbst. Doch die meisten benötigen Interprozesskommunikation und temporäre Dateien. Außerdem blähen sie das Skript auf, bis am Ende die eigentlichen Jobs kaum noch zu finden sind. Das möchte man nicht an einen Kollegen weitergeben.

Synchronisation mit Pipes

Es gibt allerdings eine einfache Möglichkeit, Jobs in der Shell gleichzeitig zu starten, bei der die Shell automatisch wartet, bis der letzte der Jobs geendet hat: Prozesse, die über Pipes miteinander verbunden sind, startet die Shell gleichzeitig.

Die Standardausgabe jeder dieser Jobs ist mit der Standardeingabe des nächsten verbunden, und die Standardeingabe mit der Standardausgabe des vorigen Jobs. Auf diese Art reichen die Ausgaben aber nur zum jeweils nächsten Job, und die Shell sieht nur die Ausgabe des jeweils letzten. Die Jobs davor würden zudem blockieren, wenn ihre Ausgabe nicht gelesen wird.

Nun ließe sich die Ausgabe der Jobs mit cat in eine Datei leiten und diese so entsperren:

( cat >> $logfile &; jobx )

Damit würden zwar alle Jobs problemlos laufen. Aber die Ausgabe würde wieder durcheinander in die Logdatei geschrieben. Besser ist es, wenn jeder Job die Ausgabe des vorigen Jobs an den nächsten weiterleiten würde. Das heißt, jeder Job, mit Ausnahme des ersten, müsste etwa so aussehen:

( jobx; cat )

Die runden Klammern schicken die Jobs in eine Subshell, sodass das Semikolon nicht die Pipe auseinanderreißt. Auf diese Weise erscheinen die Ausgaben allerdings in umgekehrter Reihenfolge:

$ echo 1|(echo 2; cat)|(echo 3; cat)
3
2
1

Außerdem kann Job Nummer 2 seine Ausgabe erst vollständig schreiben, wenn Job 3 beendet ist und cat die Ausgabe weiterleitet. Dito für Job 1: Zwar starten alle Jobs zur gleichen Zeit, aber die ersten blockieren, bis die letzten fertig sind. Das lässt sich zwar lösen, wenn die Reihenfolge des cat-Befehls und des Jobs getauscht ist:

$ echo 1|(cat; echo 2)|(cat; echo 3)
1
2
3

Nun stimmt zwar die Reihenfolge. Aber Job 2 startet erst, wenn Job 1 seine Ausgabe schließt. Und Job 3 startet erst, wenn Job 2 seine Standardausgabe schließt.

Kommentare (Insgesamt: 3 || Alle anzeigen || Kommentieren )
Pro-Linux
Traut euch!
Neue Nachrichten
Werbung