(Bash) Skript A, warten Sie auf Skript B, aber nicht auf den untergeordneten Prozess

9

Also habe ich scriptA, das Folgendes tut:

ssh server1 -- scriptB &
ssh server2 -- scriptB &
ssh server3 -- scriptB &
wait
otherstuffhappens

ScriptB macht:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
exit

Mein gewünschtes Ergebnis ist, dass scriptA auf den Abschluss aller Instanzen von scriptB wartet, bevor es fortfährt, was derzeit der Fall ist. Es wartet jedoch auch auf die Hintergrundsynchronisierungen der nicht so wichtigen Dinge. Dies sind größere Dateien, auf die ich nicht warten möchte.

Ich habe den Unterschied zwischen nohup, disown und & durchgelesen und verschiedene Kombinationen ausprobiert, aber ich erhalte nicht das gewünschte Ergebnis.

An diesem Punkt bin ich ziemlich ratlos. Jede Hilfe wäre dankbar!

Apoc
quelle

Antworten:

15

Das Problem hierbei ist, dass sshdauf das Ende der Datei in der Pipe gewartet wird, aus der das stdout des Befehls (aus irgendeinem Grund nicht das stderr, zumindest mit der Version, auf der ich teste) gelesen wird. Und der Hintergrundjob erbt einen fd an diese Pipe.

Um dies zu umgehen, leiten Sie die Ausgabe dieses Hintergrundbefehls rsyncin eine Datei um oder /dev/nullwenn Sie sich nicht darum kümmern. Sie sollten stderr auch umleiten, denn selbst wenn sshd nicht auf die entsprechende Pipe wartet, wird die Pipe nach dem Beenden sshdunterbrochen und rsyncwürde getötet, wenn versucht wird, auf stderr zu schreiben.

So:

rsync ... > /dev/null 2>&1 &

Vergleichen Sie:

$ time ssh localhost 'sleep 2 &'
ssh localhost 'sleep 2 &'  0.05s user 0.00s system 2% cpu 2.365 total
$ time ssh localhost 'sleep 2 > /dev/null &'
ssh localhost 'sleep 2 > /dev/null &'  0.04s user 0.00s system 12% cpu 0.349 total

Und:

$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null &'; sleep 2; cat out
141  # ls by killed with SIGPIPE upon writing the error message
$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null 2>&1 &'; sleep 2; cat out
2    # ls exited normally after writing the error on /dev/null instead
     # of a broken pipe
Stéphane Chazelas
quelle
4

Schreiben Sie ein Skript, das beiden übergeordnet ist, und steuern Sie dann problemlos beide . Alternativ können Sie einen Kommunikationskanal zwischen den beiden Kanälen einrichten .

Um bestimmte Hintergrundjobs gezielt zu ignorieren, können Sie die PID ( $!die letzte PID wait $pidfür Hintergrundjobs) erfassen und nur auf den Abschluss dieses Jobs warten.

l0b0
quelle
3

Das -fFlag zur sshBehebung des Problems. Getestet am:

#!/bin/sh -e
echo $$_script__begin
( echo sleeping; sleep 2; echo slept )&
echo $$_script__end

Wenn ich es mit ssh localhost ./scriptstarte, wartet es, bis es sleptauftaucht. Mit dem -fFlag wird es beendet echo $$_script__endund sleptspäter im Hintergrund angezeigt, nachdem der sshBefehl zurückgegeben wurde.

PSkocik
quelle
2

Dies ist ein bekanntes Problem des OpenSSH-Servers, das in Upstream- Bugzilla # 2071 beschrieben und diskutiert wird . In dem Fehler werden mehrere Problemumgehungen entweder auf der OpenSSH-Seite, aber auch für das Skript vorgeschlagen.

Wenn Sie für die Ausgabe des Skripte warten wollen, sollten Sie eine hinzufügen , waitvor exitder scriptBauch.

Wenn Sie sich nicht für die Ausgabe interessieren, verwenden Sie eine Variation von nohupund E / A-Umleitung zu /dev/null, um das Problem auf die gleiche Weise zu beheben.

Jakuje
quelle
1

Sie können dies versuchen. $!ist eine Standard-Shell-Variable, die die Prozess-ID der zuletzt ausgeführten Hintergrund-Pipeline / des zuletzt ausgeführten Prozesses enthält.

command1 &
lpid1=$!

command2 &
lpid2=$!

command3 &
lpid=$!

wait $lpid1  # waits for only the process with PID lpid1  to complete. 

Sie müssen dies gemäß Ihrem Skript verwenden, indem Sie eine exportVariable usw. Verwenden

Kaushik Nayak
quelle
1

B muss nur auf seine eigenen Hintergrundprozesse warten:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
wait
exit
Jared Lovell
quelle
An diesem Punkt können Sie den zweiten Rsync genauso gut nicht im Hintergrund ausführen und die Verwendung waitvollständig vermeiden . Obwohl ich vermute, dass das OP beide rsyncProzesse parallel ausführen wollte , würde dies bedeuten, dass beide (mit &) hinterlegt und dann verwendet werden wait. Auf jeden Fall stimme ich zu, dass dies der einfachste Weg ist, das Problem zu beheben, und der, den ich basierend auf den Informationen in der Frage auswählen würde.
David Z