Kapitel 6 Funktionen
Je länger die Shellscripts in den vorangegangenen Kapiteln wurden, umso unübersichtlicher wurden sie. Verwendete man dann auch noch die Routinen mehrmals im Script, so steigerte sich der Umfang weiter. In solch einem Fall (und eigentlich fast immer) sind Funktionen die Lösung. Mit Funktionen fassen Sie mehrere Befehle in einem Block zwischen geschweiften Klammern zusammen und rufen sie bei Bedarf mit einem von Ihnen definierten Funktionsnamen auf. Eine einmal definierte Funktion können Sie jederzeit in Ihrem Script mehrmals aufrufen und verwenden. Funktionen sind somit auch Scripts, welche in der laufenden Umgebung des Shell-Prozesses immer präsent sind, und verhalten sich folglich, als seien Sie interne Shell-Kommandos.
6.1 Definition
Wenn Sie eine Shell-Funktion schreiben, wird diese zunächst nicht vom Shell-Prozess ausgeführt, sondern zunächst in der laufenden Umgebung gespeichert (genauer, die Funktion wird hiermit definiert). Mit folgender Syntax können Sie eine Funktion definieren:
funktions_name() {
kommando1
kommando2
...
kommando_n
}
Sie geben nach einem frei wählbaren Funktionsnamen runde Klammern an. Die nun folgenden Kommandos, welche diese Funktion ausführen soll, werden zwischen geschweifte Klammern gesetzt. Folgende Aspekte müssen Sie allerdings bei der Definition einer Funktion beachten:
|
funktions_name und die Klammerung () (bzw. das gleich anschließend gezeigte Schlüsselwort function und funktions_name) müssen in einer Zeile stehen. |
|
Vor der sich öffnenden geschweiften Klammer »{« muss sich mindestens ein Leerzeichen oder ein Newline-Zeichen befinden. |
|
Vor einer sich schließenden geschweiften Klammer »}« muss sich entweder ein Semikolon oder ein Newline-Zeichen befinden. |
Somit lässt sich eine Funktion wie folgt auch als Einzeiler definieren:
funktions_name() { kommando1 ; kommando2 ; ... ; kommando_n ; }
6.1.1 Definition (Bash und Korn-Shell only)
In der Bash und der Korn-Shell steht Ihnen hierzu mit dem Schlüsselwort function noch die folgende Syntax zur Verfügung, um eine Funktion zu definieren:
function funktions_name {
kommando1
kommando2
...
kommando_n
}
Im Unterschied zur herkömmlichen Syntax, die für alle Shells gilt, fallen beim Schlüsselwort function die runden Klammern hinter dem Funktionsnamen weg.
6.1.2 Funktionsaufruf
Funktionen müssen logischerweise immer vor dem ersten Aufruf definiert sein, da ein Script ja auch von oben nach unten, Zeile für Zeile, abgearbeitet wird. Daher befindet sich die Definition einer Funktion immer am Anfang des Scripts bzw. vor dem Hauptprogramm.
Wird im Hauptprogramm die Funktion aufgerufen, was durch die Notierung des Funktionsnamens geschieht, werden die einzelnen Befehle im Funktionsblock ausgeführt.
# Demonstriert einen einfachen Funktionsaufruf
# Name: afunc1
# Die Funktion hallo
hallo() {
echo "In der Funktion hallo()"
}
# Hier beginnt das Hauptprogramm
echo "Vor der Ausführung der Funktion ..."
# Jetzt wird die Funktion aufgerufen
hallo
# Nach einem Funktionsaufruf
echo "Weiter gehts im Hauptprogramm"
Das Script bei der Ausführung:
you@host > ./afunc1
Vor der Ausführung der Funktion ...
In der Funktion hallo()
Weiter gehts im Hauptprogramm
Die Shell-Funktion wird von der laufenden Shell ausgeführt und ist somit auch Bestandteil des aktuellen Prozesses (es wird keine Subshell verwendet). Im Unterschied zu vielen anderen Programmiersprachen ist der Zugriff auf die Variablen innerhalb eines Shellscripts auch in Funktionen möglich, obwohl sie hier nicht definiert wurden. Dies bedeutet auch: Wird eine Variable in der Funktion modifiziert, gilt diese Veränderung ebenso für das Hauptprogramm. Das folgende Script demonstriert dies:
# Demonstriert einen einfachen Funktionsaufruf
# Name: afunc2
# Die Funktion print_var
print_var() {
echo $var # test
var=ein_neuer_Test # var bekommt neuen Wert
}
var=test
echo $var # test
print_var # Funktionsaufruf
echo $var # ein_neuer_Test
Das Script bei der Ausführung:
you@host > ./afunc2
test
test
ein_neuer_Test
Weil eine Shell-Funktion dem eigenen Prozess zugeteilt ist, können Sie diese genauso wie eine Variable mit unset wieder entfernen.
# Demonstriert einen einfachen Funktionsaufruf
# Name: afunc3
# Die Funktion print_var
print_var() {
echo $var # test
var=ein_neuer_Test # var bekommt neuen Wert
}
var=test
echo $var # test
print_var # Funktionsaufruf
echo $var # ein_neuer_Test
unset print_var # Funktion löschen
print_var # Fehler!!!
Das Script bei der Ausführung:
you@host > ./afunc3
test
test
ein_neuer_Test
./afunc1: line 16: print_var: command not found
Hinweis Bei Korn-Shell und Bash ist mit unset zum Löschen der Funktionen die Option -f erforderlich, weil es hier erlaubt ist, dass Funktionen und Variablen denselben Namen haben dürfen. Existiert hier eine Variable mit dem gleichen Namen, wird die Variable gelöscht und nicht – wie vielleicht beabsichtigt – die Funktion. Die Bourne-Shell hingegen erlaubt keine gleichnamigen Bezeichnungen von Funktionen und Variablen.
|
6.1.3 Funktionen exportieren
Wenn Funktionen wie einfache Variablen mit unset wieder gelöscht werden können, werden Sie sich sicherlich fragen, ob man Funktionen auch in Subprozesse exportieren kann. Allgemein gibt es keine Möglichkeit, Shell-Funktionen zu exportieren. Ausnahme ist wieder die Bash: Hier können Sie mittels export –f funktions_name eine Funktion zum Export freigeben.
Bourne-Shell und Korn-Shell hingegen können keine Funktionen exportieren. Hierbei steht Ihnen nur der Umweg über den Punkteoperator mittels
. functions_name
zur Verfügung. Dabei muss die Funktion in einer Datei geschrieben und mittels . functions_name (oder auch source functions_name) eingelesen werden. Ein Beispiel:
# Name: afunc_ex
echo "Rufe eine externe Funktion auf ..."
. funktionen
echo "... ich bin fertig"
Im Beispiel wird die Datei »funktionen« in das Script (keiner Subshell) eingelesen und ausgeführt. Die Datei und Funktion sieht wie folgt aus:
# Name: funktionen
# Funktion localtest
afunction() {
echo "Ich bin eine Funktion"
}
Das Script bei der Ausführung:
you@host > ./afunc_ex
Rufe eine externe Funktion auf ...
... ich bin fertig
Hinweis Auch wenn mit Bourne- und Korn-Shell keine echten Exporte umsetzbar sind, so stehen den Subshells, welche durch eine Kopie des Eltern-Prozesses entstehen, auch die Funktionen des Elternprozesses zur Verfügung und werden mitkopiert. Eine solche Subshell wird erstellt mit `...`, (...) und einem direkten Scriptaufruf. Natürlich setzt ein direkter Scriptaufruf voraus, dass keine Shell vorangestellt wird (bspw. ksh scriptname) und dass sich auch in der ersten Zeile nichts bezogen auf eine andere Shell befindet (bspw. #!/bin/ksh). Bei der Korn-Shell müssen Sie außerdem noch typeset -fx functions_name angeben, damit den Subshells auch hier die aktuellen Funktionen des Eltern-Prozesses zur Verfügung stehen. Denn dies geschieht im Gegensatz zur Bourne-Shell bei der Korn-Shell nicht automatisch.
|
Funktionsbibliotheken
Der Vorgang, der Ihnen eben mit dem Punkteoperator demonstriert wurde, wird ebenso verwendet, wenn Sie eine Funktionsbibliothek erstellen wollen, in der sich immer wiederkehrende kleine Routinen ansammeln. Damit können Sie mit Ihrem Script die gewünschte Bibliotheksdatei einlesen und so auf alle darin enthaltenen Funktionen zugreifen. Der Aufruf erfolgt dabei aus Ihrem Script mit (der Bibliotheksname sei stringroutinen.bib):
. stringroutinen.bib
Hier wird natürlich davon ausgegangen, dass sich die Script-Bibliotheksdatei im selben Verzeichnis wie das laufende Script befindet. Nach diesem Aufruf im Script stehen Ihnen die Funktionen von stringroutine.bib zur Verfügung.
Hinweis Wenn Sie jetzt motiviert sind, eigene Bibliotheken zu schreiben, so bedenken Sie bitte den Umfang einer solchen Datei. Schließlich muss die komplette Bibliotheksdatei eingelesen werden, nur um meistens ein paar darin enthaltene Funktionen auszuführen. Daher kann es manchmal ratsam sein, eher über Copy & Paste die nötigen Funktionen in das aktuelle Script einzufügen – was allerdings dann wiederum den Nachteil hat, dass Änderungen an der Funktion an mehreren Orten vorgenommen werden müssen.
|
6.1.4 Aufrufreihenfolge
Sollten Sie einmal aus Versehen oder mit Absicht eine Funktion schreiben, zu der es ebenfalls ein internes Shell-Kommando oder gar ein externes Kommando gibt, richtet sich die Shell nach folgender Aufrufreihenfolge: Existiert eine selbst geschriebene Shell-Funktion mit entsprechenden, aufgerufenen Namen, wird sie ausgeführt. Gibt es keine Shell-Funktion, wird das interne Shell-Kommando (Builtin) bevorzugt. Gibt es weder eine Shell-Funktion noch ein internes Shell-Kommando, wird nach einem externen Kommando in den Suchpfaden (PATH) gesucht. Also nochmals die Reihenfolge:
2. |
Internes Shell-Kommando |
|
|
3. Externes Kommando (in PATH)
6.1.5 Who is who
Wissen Sie nicht genau, um was es sich bei einem Kommando denn nun handelt, können Sie dies mit type abfragen, beispielsweise:
you@host > hallo() {
> echo "Hallo"
> }
you@host > type hallo
hallo is a function
hallo ()
{
echo "Hallo"
}
you@host > type echo
echo is a shell builtin
you@host > type ls
ls is aliased to `/bin/ls $LS_OPTIONS`
you@host > type ps
ps is hashed (/bin/ps)
you@host > type type
type is a shell builtin
6.1.6 Aufruf selbst bestimmen
Haben Sie zum Beispiel eine Funktion, von der Sie eine Shell-Funktion, ein internes Shell-Kommando und ein externes Kommando auf Ihrem Rechner kennen, so können Sie auch hierbei selbst bestimmen, was ausgeführt werden soll. Einfachstes Beispiel ist echo, von dem sowohl ein Shell-internes als auch ein externes Kommando existiert. Schreiben Sie bspw. noch eine eigene echo-Funktion, dann hätten Sie hierbei drei verschiedene Varianten auf Ihrem System zur Verfügung.
Der Aufrufreihenfolge nach wird ja die selbst geschriebene Shell-Funktion bevorzugt, sodass Sie diese weiterhin mit dem einfachen Aufruf ausführen können. Wollen Sie allerdings jetzt das interne Shell-Kommando (Builtin) verwenden, müssen Sie dies der Shell mit dem Schlüsselwort builtin mitteilen, damit nicht die Shell-Funktion ausgeführt wird:
you@host > builtin echo "Hallo Welt"
Hallo Welt
Wollen Sie hingegen das externe Kommando echo aufrufen, müssen Sie den absoluten Pfad zu echo verwenden:
you@host > /bin/echo "Hallo Welt"
Hallo Welt
Warum sollten Sie bei echo das externe Kommando verwenden, wo beide doch dieselbe Funktionalität haben? Versuchen Sie einmal, das interne Kommando mit ––help um Hilfe zu bitten, versuchen Sie es dann beim externen.
6.1.7 Funktionen auflisten
Wollen Sie auflisten, welche Funktionen in der laufenden Shell zurzeit definiert sind, können Sie sich mit set oder typeset einen Überblick verschaffen (siehe Tabelle 6.1).
Tabelle 6.1
Auflisten definierter Funktionen
Kommando
|
Shells
|
Bedeutung
|
set
|
sh, bash
|
Gibt alle Variablen und Funktionen mit kompletter Definition aus
|
typeset –f
|
ksh, bash
|
Listet alle Funktionen mit kompletter Definition auf
|
typeset –F
|
ksh, bash
|
Listet alle Funktionen ohne Definition auf
|
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.
|