Frage

Kontext:

Ich habe ein Bash-Skript, das eine Subshell und einen Trap für das EXIT-Pseudosignal enthält, und es fängt Interrupts während eines nicht richtig ab rsync.Hier ist ein Beispiel:

#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir

cleanup () {
     echo "Cleaning up!"
     #do stuff
     trap - EXIT 
}

trap '{
    (cleanup;) | 2>&1 tee -a $logfile
}' EXIT

(
    #main script logic, including the following lines:
    (exec sleep 10;);        
    (exec rsync --progress -av --delete $directory1 /var/tmp/$directory2;);

)  | 2>&1 tee -a $logfile
trap - EXIT #just in case cleanup isn't called for some reason

Die Idee des Skripts ist dies:der größte Teil der wichtigen Logik läuft in einer Subshell, die durchgeleitet wird tee und zu einer Logdatei, damit ich nicht muss tee jede einzelne Zeile der Hauptlogik, um alles zu protokollieren.Immer wenn die Subshell endet oder das Skript aus irgendeinem Grund gestoppt wird (das EXIT-Pseudosignal sollte alle diese Fälle erfassen), fängt der Trap es ab und führt das aus cleanup() funktion, und entfernen sie dann die falle.Der rsync und sleep befehle (der sleep ist nur ein Beispiel) werden durchlaufen exec um die Erstellung von Zombie-Prozessen zu verhindern, wenn ich das übergeordnete Skript beende, während sie ausgeführt werden, und jeder potenziell lang laufende Befehl in eine eigene Subshell eingeschlossen wird, damit wann exec beendet wird, wird das gesamte Skript nicht beendet.

Problem:

Wenn ich das Skript unterbreche (via kill oder STRG + C) während der exec / Subshell gewickelt sleep befehl, die Falle funktioniert einwandfrei, und ich sehe "Aufräumen!" echo und protokolliert.Wenn ich das Skript während des unterbreche rsync befehl, ich verstehe rsync ende und schreibe rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6] auf den Bildschirm, und dann stirbt das Skript einfach;kein Aufräumen, kein Einfangen.Warum unterbricht / tötet man nicht rsync die Falle auslösen?

Ich habe versucht, das zu verwenden --no-detach wechseln Sie mit rsync, aber es hat nichts geändert.Ich habe Bash 4.1.2, rsync 3.0.6, CentOS 6.2.

War es hilfreich?

Lösung

Wie wäre es, wenn einfach die gesamte Ausgabe von Punkt X zu tee umgeleitet wird, ohne sie überall wiederholen und mit allen Sub-Shells und Execs herumspielen zu müssen...(hoffe, ich habe nichts verpasst)

#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir

exec > >(exec tee -a $logfile) 2>&1

cleanup () {
     echo "Cleaning up!"
     #do stuff
     trap - EXIT 
}
trap cleanup EXIT

sleep 10
rsync --progress -av --delete $directory1 /var/tmp/$directory2

Andere Tipps

Neben set -e, Ich denke du willst set -E:

Wenn gesetzt, wird jede Falle bei FEHLER von Shell‐Funktionen, Befehlsersetzungen und Befehlen geerbt, die in einer Sub-Shell-Umgebung ausgeführt werden.Die Fehlerfalle wird in solchen Fällen normalerweise nicht vererbt.

Alternativ können Sie Ihre Befehle nicht in Unterschalen einschließen, sondern geschweifte Klammern verwenden, die Ihnen weiterhin die Möglichkeit geben, Befehlsausgaben umzuleiten, sie jedoch in der aktuellen Shell auszuführen.

Die Unterbrechung wird ordnungsgemäß abgefangen, wenn Sie der Falle INT hinzufügen

trap '{
    (cleanup;) | 2>&1 tee -a $logfile
}' EXIT INT

Bash fängt Interrupts korrekt ab.Dies beantwortet jedoch nicht die Frage, warum das Skript beim Beenden abfängt, wenn sleep unterbrochen wird oder warum es nicht ausgelöst wird rsync, aber lässt das Skript so funktionieren, wie es soll.Hoffe das hilft.

Ihre Shell ist möglicherweise so konfiguriert, dass sie bei einem Fehler beendet wird:

bash # enter subshell
set -e
trap "echo woah" EXIT
sleep 4

Wenn Sie unterbrechen sleep (^ C) dann wird die Subshell aufgrund von beendet set -e und drucken woah im Prozess.

Auch etwas unabhängig:Ihrer trap - EXIT befindet sich (explizit) in einer Subshell, sodass sie nach der Rückkehr der Bereinigungsfunktion keine Auswirkung hat

Es ist ziemlich klar aus Experimenten, dass rsync verhält sich wie andere Tools wie ping und erben Sie keine Signale vom aufrufenden Bash-Elternteil.

Sie müssen also ein wenig kreativ werden und so etwas wie das Folgende tun:

$ cat rsync.bash
#!/bin/sh

 set -m
 trap '' SIGINT SIGTERM EXIT
 rsync -avz LargeTestFile.500M root@host.mydom.com:/tmp/. &
 wait

 echo FIN

Jetzt, wenn ich es laufen lasse:

$ ./rsync.bash
X11 forwarding request failed
building file list ... done
LargeTestFile.500M
^C^C^C^C^C^C^C^C^C^C
sent 509984 bytes  received 42 bytes  92732.00 bytes/sec
total size is 524288000  speedup is 1027.96
FIN

Und wir können sehen, dass die Datei vollständig übertragen wurde:

$ ll -h | grep Large
-rw-------. 1  501 games 500M Jul  9 21:44 LargeTestFile.500M

Wie es funktioniert

Der Trick hier ist, dass wir Bash via erzählen set -m so deaktivieren Sie Jobsteuerelemente für alle darin enthaltenen Hintergrundjobs.Wir hintergründen dann die rsync und dann läuft ein wait befehl, der auf den letzten Ausführungsbefehl wartet, rsync, bis es vollständig ist.

Wir bewachen dann das gesamte Skript mit dem trap '' SIGINT SIGTERM EXIT.

Verweis

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top