2013-08-28 6 views
11

script1.sh:Bash: Warum endet das übergeordnete Skript nicht in SIGINT, wenn das untergeordnete Skript SIGINT abfängt?

#!/bin/bash  

./script2.sh 
echo after-script 

script2.sh:

#!/bin/bash 

function handler { 
    exit 130 
} 

trap handler SIGINT 

while true; do true; done 

Wenn I script1.sh von einem Terminal zu starten, und dann Strg + C verwendet SIGINT seine Prozeßgruppe zu senden, wobei das Signal wird von script2.sh abgefangen und wenn script2.sh beendet wird, gibt script1.sh "after-script" aus. Allerdings hätte ich erwartet, dass script1.sh sofort nach der Zeile beendet wird, die script2.sh aufruft. Warum ist das in diesem Beispiel nicht der Fall?

Zusätzliche Bemerkungen (edit):

  • Als script1.sh und script2.sh in derselben Prozessgruppe sind, SIGINT wird zu beiden Scripts gesendet, wenn Strg + C in der Befehlszeile gedrückt wird . Deshalb würde ich nicht erwarten, dass script1.sh fortgesetzt wird, wenn script2.sh beendet wird.

  • Wenn die Zeile "Trap-Handler SIGINT" in script2.sh auskommentiert ist, wird script1.sh sofort beendet, nachdem script2.sh existiert. Ich möchte wissen, warum es sich dann anders verhält, denn script2.sh erzeugt dann genau den gleichen Exit-Code (130).

+1

Vielleicht 'set -e' verwenden? – phs

Antwort

10

Neue Antwort:

Diese Frage ist viel interessanter, als ich ursprünglich vermutet. Die Antwort ist im Wesentlichen hier gegeben:

What happens to a SIGINT (^C) when sent to a perl script containing children?

Hier ist der relevante tidbit. Mir ist klar, dass Sie nicht Perl verwenden, aber ich nehme an, dass Bash die Konvention von C verwendet.

Perl eingebaute Systemfunktion funktioniert genauso wie das C-System (3) Funktion aus der Standard-C-Bibliothek soweit Signale betroffen sind. Wenn Sie Perls Version von system() oder pipe open oder backticks verwenden, , dann wird das übergeordnete System - das einzige aufrufende System, das von aufgerufen wird - IGNORE alle SIGINT und SIGQUIT während die Kinder ausgeführt werden.

This explanation ist das Beste, was ich über die verschiedenen Möglichkeiten gesehen habe, die man machen kann. Es sagt auch, dass Bash den WCE-Ansatz macht. Das heißt, wenn ein Elternprozess SIGINT empfängt, wartet er, bis sein Kindprozess zurückkehrt. Wenn dieser Prozess von einem SIGINT aus ausgeführt wird, wird er auch mit SIGINT beendet. Wenn das Kind auf andere Weise austritt, ignoriert es SIGINT.

Es gibt auch eine Möglichkeit, dass der anrufende Schale sagen kann, ob das angerufene Programm auf SIGINT verlassen und wenn es ignoriert SIGINT (oder verwendet sie für andere Zwecke). Wie bei der WUE-Methode wartet die Shell auf das unter abgeschlossene Kind. Es zeigt an, ob das Programm auf SIGINT beendet wurde und wenn so, es das Skript beenden. Wenn das Programm einen anderen Exit ausgeführt hat, wird das Skript fortgesetzt. Ich werde den Weg der Dinge die "WCE" (für "warten und kooperative Ausfahrt") für den Rest dieses Dokuments nennen.

Ich kann einen Verweis darauf in der Bash-Man-Seite nicht finden, aber ich werde weiter in den Infodokumenten suchen. Aber ich bin zu 99% überzeugt, dass dies die richtige Antwort ist.

Alte Antwort:

Ein Nicht-Null-Exit-Status von einem Befehl in einem Bash-Skript nicht beendet das Programm. Wenn Sie eine echo $? nach ./script2.sh tun, wird 130 angezeigt. Sie können das Skript mit set -e beenden, wie von phs vorgeschlagen.

$ help set 
... 
-e Exit immediately if a command exits with a non-zero status. 
+0

Das beantwortet die Frage nicht ganz. Beachten Sie, dass SIGINT sowohl an script1.sh als auch an script2.sh gesendet wird, wenn Strg + C am Terminal gedrückt wird. Ich habe einige zusätzliche Bemerkungen zu der Frage hinzugefügt. –

+0

Das ist sehr interessant. Ich wundere mich immer noch über den Unterschied zwischen den Versionen mit und ohne "Trap Handler SIGINT". Ich vermute, dass das Signal zuerst an das Kind und dann an das Elternteil geliefert wird. Wenn ein Trap-Handler im Kind installiert ist, dauert es möglicherweise etwas länger, bis er beendet wird, und somit ist er noch am Leben, wenn das Elternsignal das Signal empfängt (das es ignoriert). Ohne einen Handler ist das Kind vielleicht schon beendet, wenn das Elternteil das Signal empfängt, es also nicht ignoriert und beendet. Kann mir jemand meine Vermutungen bestätigen? –

+0

Ihre Vermutung ist nicht korrekt. Ich habe meine Antwort aktualisiert. – seanmcl

0

Der Grund ist Ihr script1.sh nicht ist beendet, dass script2.sh in einer Sub-Shell ausgeführt wird. Um die ehemalige Skript Ausfahrt zu machen, können Sie entweder -e wie phs und seanmcl vorgeschlagen oder die script2.sh zwingen, in der gleichen Shell auszuführen, indem er sagt:

. ./script2.sh 

in Ihrem ersten Skript. Was Sie beobachten, wäre offensichtlich, wenn Sie vor der Ausführung Ihres Skripts set -x tun würden. help set sagt:

-x Print commands and their arguments as they are executed. 
0

Sie können auch Ihre zweite Skript lassen ein abschließendes Signal an seiner Parent-Skript von SIGHUP oder anderen sicheren und verwertbaren Signalen wie SIGQUIT senden, in denen die Eltern Skript betrachtet oder Falle kann auch (zum Senden SIGINT funktioniert nicht).

script1.sh:

#!/bin/bash 

trap 'exit 0' SIQUIT ## We could also just accept SIGHUP if we like without traps but that sends a message to the screen. 

./script2.sh ## or "bash script.sh" or "(. ./script.sh;) which would run it on another process 
echo after-script 

script2.sh:

#!/bin/bash 

SLEEPPID='' 

PID=$BASHPID 
read PPID_ < <(exec ps -p "$PID" -o "$ppid=") 

function handler { 
    [[ -n $SLEEPPID ]] && kill -s SIGTERM "$SLEEPPID" &>/dev/null 
    kill -s SIGQUIT "$PPID_" 
    exit 130 
} 

trap handler SIGINT 

# better do some sleeping: 

for ((;;)); do 
    [[ -n $SLEEPPID ]] && kill -s 0 "$SLEEPPID" &>/dev/null || { 
    sleep 20 & 
    SLEEPPID=$! 
    } 
    wait 
done 

Ihre ursprüngliche letzte Zeile in Ihrer script1.sh wie je Implementierung bestimmt auf Ihre Skripte auch haben könnte.

./script2.sh || exit 
... 

Oder

./script2.sh 
[[ $? -eq 130 ]] && exit 
... 
1

Der zweite Teil von @ seanmcl aktualisierten Antwort richtig ist und der Link zu http://www.cons.org/cracauer/sigint.html ist ein wirklich guter durch sorgfältig zu lesen.

Von diesem Link "Sie können den richtigen Exit-Status nicht durch einen Exit (3) mit einem speziellen numerischen Wert" fälschen ", selbst wenn Sie den numerischen Wert für Ihr System nachschlagen". Genau das versucht @Hermann Speiche script2.sh.

Eine Antwort ist Funktion Handler in script2.sh wie folgt zu ändern:

function handler { 
    # ... do stuff ... 
    trap INT 
    kill -2 $$ 
} 

Dies entfernt effektiv die Signal-Handler und „rethrows“ die SIGINT, den Bash-Prozess mit dem entsprechenden Flags beenden verursacht, so dass Sein Eltern-bash-Prozess behandelt dann korrekt das SIGINT, das ursprünglich an ihn gesendet wurde. Auf diese Weise ist die Verwendung von set -e oder eines anderen Hacks nicht erforderlich.

Es ist auch erwähnenswert, dass, wenn Sie eine ausführbare Datei haben, die sich falsch verhält, wenn ein SIGINT gesendet wird (es entspricht nicht "Wie ein richtiges Programm zu sein" in der obigen Verbindung, z.Es wird mit einem normalen Rückgabecode beendet. Eine Möglichkeit, dies zu umgehen, besteht darin, den Aufruf dieses Prozesses mit einem Skript wie dem folgenden zu umhüllen:

#!/bin/bash 

function handler { 
    trap INT 
    kill -2 $$ 
} 

trap handler INT 
badprocess "[email protected]" 
Verwandte Themen