Innerhalb von Shellscripten besteht die Möglichkeit Shellfunktionen zu definieren.
Die Definition einer Shellfunktion erfolgt mit dem Konstruktor
function <Funktionsname> {
Funktionsbody
}
Eine Shellfunktion kann genauso benutzt werden wie ein externes Shellscript, besitzt jedoch einige Vorteile.
Um den Unterschied zwischen der Anwendung eines Shellscripts und und einer Shellfunktion zu verdeutlichen, zuerst ein kleines Beispiel. Wir haben uns dazu ein kleines script namens errormsg geschrieben, dass nichts anderes tun soll, als einen Fehlertext, der als Parameter übergeben werden soll, in schöner Form auszudrucken. Eine Shellvariable ERRFLAG soll dabei angeben, ob die Meldung wirklich erscheinen soll (ERRFLAG=1) oder ob sie unterdrückt wird (ERRFLAG=0). Sollte eine Fehlermeldung ausgegeben werden, so soll die Variable ERRPLOT auf 1 gesetzt werden, ansonsten 0. Um ein bischen debugging zu betreiben, sind ein paar zusätzliche echo-Anweisungen eingefügt, um Auskunft zum Inhalt der Variablen zu geben. Das Script errormsg sieht nun so aus
#!/bin/bash
echo "ERRFLAG=${ERRFLAG}"
echo "ERRPLOT=${ERRPLOT}"
if [ "${ERRFLAG}" = "1" ]; then
echo "Fehlermeldung ${1} !!!"
ERRPLOT=1
else
ERRPLOT=0
fi
echo "ERRFLAG=${ERRFLAG}"
echo "ERRPLOT=${ERRPLOT}"
|
Ein weiteres Script setzt nun die Variablen ERRFLAG und ERRPLOT und ruft das Script errormsg auf. Das Script test2 sieht nun so aus
#!/bin/bash
ERRFLAG=1
ERRPLOT=2
echo "P1ERRFLAG=${ERRFLAG}"
echo "P1ERRPLOT=${ERRPLOT}"
errormsg test1
echo "P2ERRFLAG=${ERRFLAG}"
echo "P2ERRPLOT=${ERRPLOT}"
export ERRFLAG ERRPLOT
errormsg test2
echo "P3ERRFLAG=${ERRFLAG}"
echo "P3ERRPLOT=${ERRPLOT}"
|
Wenn wir das Script nun starten, vorausgesetzt die Ausführungsrechte und Pfadabhängigkeiten sind richtig gesetzt, dann erhalten wir folgende Ausgabe
bash-3.1$ ./test2 P1ERRFLAG=1 P1ERRPLOT=2 ERRFLAG= ERRPLOT= ERRFLAG= ERRPLOT=0 P2ERRFLAG=1 P2ERRPLOT=2 ERRFLAG=1 ERRPLOT=2 Fehlermeldung test2 !!! ERRFLAG=1 ERRPLOT=1 P3ERRFLAG=1 P3ERRPLOT=2 bash-3.1$ |
Wir können nun sehen, dass der erste Aufruf errormsg test1 keine Ausgabe zur Folge hat, obwohl im aufrufenden Script test2 die Variable ERRFLAG auf 1 gesetzt wurde.
Das Script errormsg wird in einer eigenen Subshell ausgeführt und erbt das Environment der aufrufenden Shell.
Das hat zur Folge, dass innerhalb des Scriptes errormsg die Variablen ERRFLAG und ERRPLOT unbekannt und deshalb nicht mit Werten belegt sind, obwohl sie in der aufrufenden Shell existieren.
Das Schreiben von ERRPLOT=0 innerhalb von errormsg hat auch keine Auswirkungen auf den Inhalt der Variablen ERRPLOT in der aufrufenden Shell, wie man an der Ausgabe P2ERRPLOT=2 nach der Ausführung des Scriptes errormsg sehen kann.
Also:
Etwas ander ist es nun beim zweiten Aufruf errormsg test2. Hier werden vor dem Aufruf die Variablen ERRFLAG und ERRPLOT als Umgebungsvariablen in das Environment der Parentshell geladen. Beim Aufruf des scriptes errormsg erbt die Subshell nun das gesamte Environment der Parentshell und damit sind in der Subshell die Variablen ERRFLAG und ERRPLOT ebenfalls bekannt. Dies kann man an der Ausgabe ERRFLAG=1 und ERRPLOT=2 unmittelbar vor dem Ausgabetext sehen. Deshalb wird die Meldung nun auch ausgegeben. Danach wird die Variable ERRPLOT auf 1 gesetzt und wieder in die Parentshell gewechselt. Wie man dort aber an P3ERRPLOT=2 sehen kann, hat sich der Wert von ERRPLOT in der Parentshell nicht geändert. Also:
Dieser Mechanismus ist bei vielen Usern auch der Grund für Frust.
In der Dokumentation einer Anwendung lesen sie zB, dass in die Shell-Umgebung eine Variable mit einem bestimmten Wert geladen werden muss.
Das könnte zB. VISUAL=/usr/bin/joe sein. Sie öffnen nun ein Terminal und geben export VISUAL=/usr/bin/joe ein und schliessen das Terminal wieder.
Das geschieht in der irrigen Annahme, es sei nun eine ominöse Systemvariable mit diesem Wert gesetzt worden, aber ganau das ist falsch, die Anwendung wird immer noch nicht wie gewünscht funktionieren und wenn sie ein Terminal öffnen und mit echo $VISUAL nachschauen, dann steht da immer noch nicht der gewünschte Inhalt drin.
In Wirklichkeit ist es so, dass das Environment nur in der Shell gilt, in der es erzeugt wurde und in allen Subshells, die von dieser Parentshell aus aufgerufen werden.
Wenn sie also eine Shell öffnen, die Einstellung export VISUAL=... vornehmen und die Shell dann beenden, dann ist die Einstellung auch in diesem Moment verloren gegangen, denn die Shell existiert nicht mehr und wird auch keine Subshell mehr starten, an das sie das Environment übergeben könnte.
Deshalb muss also der export-Aufruf in einer Parentshell der Anwendung selbst ausgeführt werden.
Das kann entweder durch ein kleines Script passieren, das die Anwendung startet und die Environment-Variable vorher setzt, oder indem man den export-Eintrag in einer anderen Parentshell einträgt (zb eine der Systemstartdateien oder Shellstartdateien, wie zB. /etc/profile usw).
Mit Shellfunktionen ist die Kommunikation zwischen aufrufendem Script und der eingebetteten Shellfunktion wesentlich komfortabler, da alles in der selben Shell läuft.
Dazu schreiben wir das Script test2 um, das Script errormsg benötigen wir nicht mehr.
Der Inhalt von test2 wird wie folgt abgeändert:
#!/bin/bash
function errormsg {
echo "F1ERRTEST=${ERRTEST}"
echo "F1ERRFLAG=${ERRFLAG}"
echo "F1ERRPLOT=${ERRPLOT}"
local ERRTEST
echo "F2ERRTEST=${ERRTEST}"
ERRTEST=6
if [ "${ERRFLAG}" = "1" ]; then
echo "Fehlermeldung ${1} !!!"
ERRPLOT=1
else
ERRPLOT=0
fi
echo "F3ERRFLAG=${ERRFLAG}"
echo "F3ERRPLOT=${ERRPLOT}"
echo "F3ERRTEST=${ERRTEST}"
return 0
}
ERRFLAG=1
ERRPLOT=2
ERRTEST=7
echo "P1ERRFLAG=${ERRFLAG}"
echo "P1ERRPLOT=${ERRPLOT}"
echo "P1ERRTEST=${ERRTEST}"
errormsg test1
echo "P2ERRFLAG=${ERRFLAG}"
echo "P2ERRPLOT=${ERRPLOT}"
echo "P2ERRTEST=${ERRTEST}"
|
Und bei der Ausführung des Scripts erscheint:
bash-3.1$ ./test2 P1ERRFLAG=1 P1ERRPLOT=2 P1ERRTEST=7 F1ERRTEST=7 F1ERRFLAG=1 F1ERRPLOT=2 F2ERRTEST= Fehlermeldung test1 !!! F3ERRFLAG=1 F3ERRPLOT=1 F3ERRTEST=6 P2ERRFLAG=1 P2ERRPLOT=1 P2ERRTEST=7 bash-3.1$ |
An den Ausgaben F1ERRTEST=7, F1ERRFLAG=1 und F1ERRPLOT=2 kann man nun sehr schön erkennen, dass die Variablen ERRFLAG, ERRPLOT und ERRTEST auch innerhalb der Shellfunktion als globale Variablen bekannt sind.
Eine Besonderheit ist nun ERRTEST, diese Variable wird innerhalb der Shellfunktion als locale Variable deklariert und ist damit eigenständig und geht auch mit dem Verlassen der Shellfunktion verloren.
Die Anzeige F2ERRTEST= zeigt deshalb nach dem Aufruf von local ERRTEST auch an, dass die Variable ERRTEST nicht gesetzt ist. Das hat keine Auswirkungen auf die Variable ERRTEST des aufrufenden Shellscripts, der Inhalt 7 bleibt unverändert.
Deshalb wird auch die Veränderung der Variable ERRPLOT=1 in der Shellfunktion übernommen und die Variable ERRTEST zeigt nach dem Abarbeiten der Shellfunktion wieder den Wert 7 an, obwohl der Wert der Variable ERRTEST in der Shellfunktion verändert wurde.
Die Parameterübergabe geschieht wieder über die Positionsparameter $0, $1,......
$0 enthält den Namen des Scriptes, nicht der Shellfunktion und die Parameter $1, $2 ..... usw die Positionsparameter der Shellfunktion, sind also locale-Variablen!!
Mit der Anweisung return kann dem aufrufenden Shellscript wieder über den exit-Code Meldung gemacht werden und eine Umleitung von Datenströmen in die Shellfunktion und aus der Shellfunktion heraus ist ebenfalls möglich.