Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
Shell-Programmierung von Jürgen Wolf
Einführung, Praxis, Referenz
Buch: Shell-Programmierung

Shell-Programmierung
782 S., mit CD, 44,90 Euro
Galileo Computing
ISBN 3-89842-683-1
gp Kapitel 7 Signale
  gp 7.1 Grundlagen zu den Signalen
  gp 7.2 Signale senden – kill
  gp 7.3 Eine Fallgrube für Signale – trap
    gp 7.3.1 Einen Signalhandler (Funktion) einrichten
    gp 7.3.2 Mit Signalen Schleifendurchläufe abbrechen
    gp 7.3.3 Mit Signalen das Script beenden
    gp 7.3.4 Das Beenden der Shell (oder eines Scripts) abfangen
    gp 7.3.5 Signale ignorieren
    gp 7.3.6 Signale zurücksetzen


Galileo Computing

7.3 Eine Fallgrube für Signale – tradowntop

Das Kommando trap ist das Gegenstück zu kill. Damit können Sie in Ihren Shellscripts auf Signale reagieren (bzw. sie abfangen), um so den Programmabbruch zu verhindern.

trap 'kommando1; ... ; kommaond_n' Signalnummer

Dem Kommandonamen trap folgt hier ein Befehl oder eine Liste von Befehlen, die ausgeführt werden sollen, wenn das Signal mit »Signalnummer« eintrifft. Die Befehle werden zwischen einfache Single Quotes geschrieben. Trifft beim ausführenden Script ein Signal ein, wird die Ausführung an der aktuellen Position unterbrochen und die angegebenen Befehle der trap-Anweisung werden ausgeführt. Danach wird mit der Ausführung des Scripts an der unterbrochenen Stelle wieder fortgefahren (siehe Abbildung 7.2).


Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 7.2   Abfangen von Signalen mit trap


Ein einfaches Beispiel:

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap1
# Signal SIGINT (2) (Strg+C)
trap 'echo SIGINT erhalten' 2
i=0
while [ $i -lt 5 ]
do
   echo "Bitte nicht stören!"
   sleep 2
   i=`expr $i + 1`
done

Das Script bei der Ausführung:

you@host > ./trap1
Bitte nicht stören!
Bitte nicht stören! (Strg)+(C)
SIGINT erhalten
Bitte nicht stören!
Bitte nicht stören! (Strg)+(C)
SIGINT erhalten
Bitte nicht stören!
you@host >

Mit der trap-Anweisung zu Beginn des Scripts sorgen Sie dafür, dass beim Eintreffen des Signals SIGINT – entweder durch die Tastenkombination (Strg)+(C) oder mit kill –SIGINT PID_von_trap1 ausgelöst – der echo-Befehl ausgeführt wird. Würden Sie hier keine trap-Anweisung verwenden, so würde das Script beim ersten (Strg)+(C) (SIGINT) beendet werden.

Natürlich können Sie in einem Script auch mehrere Signale abfangen:

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap2
# Signal SIGINT (2) (Strg+C)
trap 'echo SIGINT erhalten' 2
# Signal SIGTERM (15) kill -TERM PID_of_trap2
trap 'echo SIGTERM erhalten' 15
i=0
while [ $i -lt 5 ]
do
   echo "Bitte nicht stören! ($$)"
   sleep 5
   i=`expr $i + 1`
done

Das Script bei der Ausführung:

you@host > ./trap2
Bitte nicht stören! (10175) (Strg)+(C)
SIGINT erhalten
Bitte nicht stören! (10175)
Bitte nicht stören! (10175)
SIGTERM erhalten
Bitte nicht stören! (10175)
Bitte nicht stören! (10175)
you@host >

Bei der Ausführung von »trap2« wurde das Signal SIGTERM hier aus einer anderen Konsole wie folgt an das Script gesendet:

you@host > kill -TERM 10175

Sie müssen allerdings nicht für jedes Signal eine extra trap-Anweisung verwenden, sondern Sie können hinter der Signalnummer weitere Signalnummern – getrennt mit mindestens einem Leerzeichen – anfügen und somit auf mehrere Signale mit denselben Befehlen reagieren.

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap3
# Signale SIGINT und SIGTERM abfangen
trap 'echo Ein Signal (SIGINT/SIGTERM) erhalten' 2 15
i=0
while [ $i -lt 5 ]
do
   echo "Bitte nicht stören! ($$)"
   sleep 5
   i=`expr $i + 1`
done

In diesem Beispiel können Sie auch erkennen, warum es nicht möglich ist und auch niemals möglich sein darf, dass das Signal SIGKILL (Nr. 9) ignoriert oder als nicht unterbrechbar eingerichtet wird. Stellen Sie sich jetzt noch eine Endlosschleife vor, die ständig Daten in eine Datei schreibt. Würde hierbei das Signal SIGKILL ausgeschaltet, so würden ewig Daten in die Datei geschrieben, bis das System oder der Plattenplatz schlapp macht, und nicht mal mehr der Systemadministrator könnte das Script unterbrechen.


Galileo Computing

7.3.1 Einen Signalhandler (Funktion) einrichten  downtop

In der Praxis werden Sie beim Auftreten eines bestimmten Signals gewöhnlich keine einfache Ausgabe vornehmen. Meistens werden Sie mit Dingen wie dem »Saubermachen« von Datenresten beschäftigt sein – oder aber, Sie richten sich hierzu einen eigenen Signalhandler (Funktion) ein, welcher auf ein bestimmtes Signal reagieren soll:

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap4
sighandler_INT() {
   printf "Habe das Signal SIGINT empfangen\n"
   printf "Soll das Script beendet werden (j/n) : "
   read
   if [[ $REPLY = "j" ]]
   then
      echo "Bye!"
      exit 0;
   fi
}
# Signale SIGINT abfangen
trap 'sighandler_INT' 2
i=0
while [ $i -lt 5 ]
do
   echo "Bitte nicht stören! ($$)"
   sleep 3
   i=`expr $i + 1`
done

Das Script bei der Ausführung:

you@host > ./trap4
Bitte nicht stören! (4843)
Bitte nicht stören! (4843) (Strg)+(C)
Habe das Signal SIGINT empfangen
Soll das Script beendet werden (j/n) : n
Bitte nicht stören! (4843)
Bitte nicht stören! (4843)
Bitte nicht stören! (4843)
you@host > ./trap4
Bitte nicht stören! (4854) (Strg)+(C)
Habe das Signal SIGINT empfangen
Soll das Script beendet werden (j/n) : n
Bitte nicht stören! (4854) (Strg)+(C)
Habe das Signal SIGINT empfangen
Soll das Script beendet werden (j/n) : j
Bye!
you@host >

Hinweis   Verwenden Sie in der Korn-Shell innerhalb von Funktionen die trap-Anweisung, dann sind die mit ihr eingerichteten Signale nur innerhalb dieser Funktion gültig.


Ein eigener Signalhandler (bzw. eine Funktion) wird häufig eingerichtet, um eine Konfigurationsdatei neu einzulesen. Bestimmt haben Sie schon einmal bei einem Dämon- oder Serverprogramm die Konfigurationsdatei Ihren Bedürfnissen angepasst. Damit die aktuellen Änderungen aktiv werden, mussten Sie die Konfigurationsdatei mittels

kill -HUP PID_of_dämon_oder_server

neu einlesen. Wie Sie dies in Ihrem Script erreichen können, haben Sie eben mit dem Script »trap4« in ähnlicher Weise gesehen. Ein einfaches Beispiel auch hierzu:

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap5
# Signal SIGHUP empfangen
trap 'readconfig' 1
readconfig() {
   . aconfigfile
}
a=1
b=2
c=3
# Endlosschleife
while [ 1 ]
do
   echo "Werte (PID:$$)"
   echo "a=$a"
   echo "b=$b"
   echo "c=$c"
   sleep 5
done

Die Datei aconfigfile sieht wie folgt aus:

# Konfigurationsdatei für trap5
# Name: aconfigfile
# Hinweis: Um hier vorgenommene Änderungen umgehend zu
# aktivieren, müssen Sie lediglich die
# Konfigurationsdatei aconfigfile mittels
# kill -HUP PID_of_trap5
# neu einlesen.
a=3
b=6
c=9

Das Script bei der Ausführung:

###--- tty1 ---###
you@host > ./trap5
Werte (PID:6263)
a=1
b=2
c=3
Werte (PID:6263)
a=1
b=2
c=3
###--- tty2 ---###
you@host > kill -HUP 6263
###--- tty1 ---###
Werte (PID:6263)
a=3
b=6
c=9 (Strg)+(C)
you@host >

Galileo Computing

7.3.2 Mit Signalen Schleifendurchläufe abbrechen  downtop

Genauso einfach können Sie auch Signale verwenden, um Schleifen abzubrechen. Hierzu müssen Sie nur in den Befehlen von trap die Anweisung break eintragen, dann wird bei Auftreten eines gewünschten Signals eine Schleife abgebrochen:

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap6
# Signale SIGINT und SIGTERM abfangen
trap 'break' 2
i=1
while [ $i -lt 10 ]
do
   echo "$i. Schleifendurchlauf"
   sleep 1
   i=`expr $i + 1`
done
echo "Nach dem $i Schleifendurchlauf abgebrochen"
echo "--- Hinter der Schleife ---"

Das Script bei der Ausführung:

you@host > ./trap6
1. Schleifendurchlauf
2. Schleifendurchlauf
3. Schleifendurchlauf
4. Schleifendurchlauf
5. Schleifendurchlauf (Strg)+(C)
Nach dem 5. Schleifendurchlauf abgebrochen
--- Hinter der Schleife ---
you@host >

Galileo Computing

7.3.3 Mit Signalen das Script beenden  downtop

Bitte beachten Sie, dass Sie mit einem abgefangenen Signal keinen Programmabbruch erreichen. Haben Sie zunächst mit der trap-Anweisung ein Signal abgefangen, müssen Sie sich gegebenenfalls selbst um die Beendigung eines Prozesses kümmern. Diese Methode wird recht häufig eingesetzt, wenn der Anwender ein Signal an den Prozess sendet, dieser aber den Datenmüll vorher noch entsorgen soll. Hierzu setzen Sie den Befehl exit an das Ende der Kommandofolge, die Sie in der trap-Anweisung angegeben haben, beispielsweise:

trap 'rm atempfile.tmp ; exit 1' 2

Hier wird beim Auftreten des Signals SIGINT zunächst die temporäre Datei atempfile.tmp gelöscht, bevor im nächsten Schritt mittels exit das Script beendet wird.


Galileo Computing

7.3.4 Das Beenden der Shell (oder eines Scripts) abfangen  downtop

Wollen Sie, dass beim Verlassen der Shell oder eines Scripts noch eine andere Datei bzw. Script ausgeführt wird, können Sie das Signal 0 (EXIT) abfangen. Dieses Signal wird beim normalen Beenden einer Shell oder eines Scripts oder über den Befehl exit gesendet, mit dem ein Script oder eine Shell vorzeitig beendet wird. Nicht abfangen können Sie hingegen mit dem Signal 0 Abbrüche, die durch kill von außen herbeigeführt wurden. Ein einfaches Beispiel, welches das Ende einer (echten) Shell abfängt:

# Name: dasEnde
# Befehle, die beim Beenden einer Shell ausgeführt werden
cat <<MARKE
********************************************
*   Hier könnten noch einige nützliche     *
*   zur Beendigung der Shell stehen.       *
********************************************
MARKE
echo "Alles erledigt – Shell mit ENTER beenden"
read

Zunächst müssen Sie in Ihrer Shell das Signal EXIT abfangen und die Datei »dasEnde« aufrufen. Steht die »Falle« für das Signal EXIT, können Sie die Shell verlassen:

you@host > trap '$SHELL $HOME/dasEnde' 0
you@host > exit
logout
********************************************
*   Hier könnten noch einige nützliche     *
*   zur Beendigung der Shell stehen.       *
********************************************
Alles erledigt – Shell mit ENTER beenden
(ENTER)
login :

Damit die Datei »dasEnde« mit wichtigen Hinweisen in Zukunft nach jedem Verlassen einer Shell dauerhaft zur Verfügung steht, sollten Sie die Zeile

trap '$SHELL $HOME/dasEnde' 0

in die Datei .profile eintragen.

Gleiches lässt sich auch in einem Shellscript verwenden:

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap7
# Signal EXIT abfangen
trap 'exithandler' 0
exithandler() {
   echo "Das Script wurde vorzeitig mit exit beendet!"
   # Hier noch anfallende Aufräumarbeiten ausführen
}
# Hauptfunktion
echo "In der Hauptfunktion" && exit 1
echo "Wird nicht mehr ausgeführt"

Das Script bei der Ausführung:

you@host > ./trap7
In der Hauptfunktion
Das Script wurde vorzeitig mit exit beendet!
you@host >

Im Gegensatz zum Vorgehen in Abschnitt 7.3.3 müssen Sie hierbei kein zusätzliches exit bei den Kommandos von trap angeben. Hier halten Sie das Script nur beim wirklichen Ende kurz auf.


Galileo Computing

7.3.5 Signale ignorieren  downtop

Wenn Sie in der Liste von Befehlen bei trap keine Angaben vornehmen, also leere Single Quotes verwenden, werden die angegebenen Signale (bzw. Signalnummern) ignoriert. Die Syntax:

trap '' Signalnummer

Somit würden Sie praktisch mit der Angabe von

trap '' 2

das Signal SIGINT beim Auftreten ignorieren. Das völlige Ignorieren von Signalen kann bei extrem kritischen Datenübertragungen sinnvoll sein. So können Sie zum Beispiel verhindern, dass beim Schreiben kritischer Systemdaten der Anwender mit einem unbedachten SIGINT reinpfuscht. Natürlich gilt hier weiterhin, dass die Signale SIGKILL und SIGSTOP nicht ignoriert werden können.


Hinweis   Laut POSIX ist das weitere Verhalten von Prozessen undefiniert, wenn die Signale SIGFPE, SIGILL oder SIGSEGV ignoriert werden und diese auch nicht durch einen manuellen Aufruf von kill ausgelöst wurden.



Galileo Computing

7.3.6 Signale zurücksetzen  toptop

Wenn Sie die Reaktion von Signalen mittels trap einmal verändert haben, können Sie durch einen erneuten Aufruf von trap den Standardzustand der Signale wiederherstellen. Hierbei reicht lediglich der Aufruf von trap und der (bzw. den) Signalnummer(n), die Sie wieder auf den ursprünglichen Zustand zurücksetzen wollen. Mehrere Signalnummern werden wieder mit mindestens einem Leerzeichen von der vorangegangenen Signalnummer getrennt.

trap Signalnummer

Das Zurücksetzen von Signalen ist sinnvoll, wenn man die Signale nur bei einem bestimmten Codeausschnitt gesondert behandeln will.

# Demonstriert die Funktion trap zum Abfangen von Signalen
# Name: trap8
# Signal SIGINT ignorieren
trap '' 2
i=0
while [ $i -lt 5 ]
do
   echo "Hier kein SIGINT möglich ..."
   sleep 1
   i=`expr $i + 1`
done
# Signal SIGINT wieder zurücksetzen
trap 2
i=0
while [ $i -lt 5 ]
do
   echo "SIGINT wieder möglich ..."
   sleep 1
   i=`expr $i + 1`
done

Das Script bei der Ausführung:

you@host > ./trap8
Hier kein SIGINT möglich ...
Hier kein SIGINT möglich ... (Strg)+(C)
Hier kein SIGINT möglich ... (Strg)+(C)
Hier kein SIGINT möglich ...
Hier kein SIGINT möglich ...
SIGINT wieder möglich ...
SIGINT wieder möglich ...  (Strg)+(C)
you@host >

Hinweis   Wollen Sie wissen, welche Signale für eine bestimmte Routine mit trap abgefangen werden, können Sie das Kommando trap ohne jegliches Argument verwenden.


 << zurück
  
  Zum Katalog
Zum Katalog: Shell-Programmierung
Shell-Programmierung
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: C von A bis Z






 C von A bis Z


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: Linux






 Linux


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux-Referenz






 Linux-Referenz


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press GmbH 2005
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de