Tracks
/
Bash
Bash
/
Lehrplan
/
Funktionen
Fu

Funktionen in Bash

{one: "1 Übung", other: "%{count} Übungen"}

Über Funktionen

Viele Bash-Skripte sind in einem streng imperativen Stil geschrieben: Führe einen Befehl aus, dann führe einen weiteren Befehl aus, und so weiter. Manchmal musst du eine Folge von Befehlen zusammenfassen, die gedanklich einen einzigen Zweck erfüllen. Genau hier kommen Funktionen ins Spiel.

Eine Funktion definieren

Du definierst eine Funktion so:

my_function () {
    COMMANDS
}

Die leeren runden Klammern zeigen lediglich an, dass du eine Funktion definierst. Zwischen sie kommt nichts hinein.

Funktionsparameter

Einmal definiert, verhalten sich Funktionen wie jeder andere Befehl (ob eingebaut oder nicht). Wie bei jedem Befehl kannst du deinen Funktionen Argumente übergeben. Innerhalb der Funktion greifst du über die Positionsparameter auf die Argumente zu, $1, $2 usw. (Erinnere dich: Die Positionsparameter haben wir im Konzept Variablen kennengelernt.)

Advanced

Der spezielle Parameter $0 wird innerhalb einer Funktion nicht geändert; er ist weiterhin der Name des laufenden Skripts. Die gerade ausgeführte Funktion kann über die Variable $FUNCNAME auf ihren Namen zugreifen.

Siehe 3.4.2 Spezielle Parameter im Handbuch.

Variablen

Du kannst Variablen innerhalb einer Funktion definieren. Wenn du die Variablen mit dem Befehl local deklarierst, ist der Gültigkeitsbereich der Variable auf die aktuelle Funktion beschränkt (und auf alle Funktionen, die von ihr aufgerufen werden). Andernfalls landet die Variable im globalen Gültigkeitsbereich.

Lokale Variablen können denselben Namen wie eine globale Variable haben. In diesem Fall „überschattet“ die lokale Variable die globale. Zum Beispiel verdeckt eine in einer Funktion deklarierte lokale Variable eine globale Variable desselben Namens: Referenzen und Zuweisungen beziehen sich auf die lokale Variable, während die globale Variable unverändert bleibt. Wenn die Funktion zurückkehrt, ist die globale Variable wieder sichtbar.

x=5

myfunc () {
    local x=100
    echo "in my function, $x == 100"
}

echo "in the global scope, $x == 5"

myfunc

echo "back in the global scope, $x == 5"

Die Ausgabe ist:

in the global scope, 5 == 5
in my function, 100 == 100
back in the global scope, 5 == 5

Innerhalb einer Funktion kannst du auf Variablen aus dem Gültigkeitsbereich des Aufrufers zugreifen. Das bedeutet, du kannst globale Variablen ebenso nutzen wie lokale Variablen, die im Aufrufer deklariert wurden (oder in irgendeiner Funktion, die den Aufrufer aufruft).

Advanced

Streng genommen ist „global“ hier nicht das richtige Wort. Um eine Variable in einer Funktion zu expandieren, geht Bash den Aufrufstapel nach oben durch, bis hinauf zum globalen Gültigkeitsbereich, um eine Funktion zu finden, in der dieser Variablenname deklariert wurde.

Dieses Beispiel ist an den Abschnitt Shell-Funktionen des Handbuchs angelehnt:

func1() {
    local var='func1 local'
    func2
}

func2() {
    echo "In func2, var = $var"
}

var=global
func1
func2

Die Ausgabe ist:

In func2, var = func1 local
In func2, var = global

Ebenso wird eine Variable beim Zuweisen eines Werts in dem Gültigkeitsbereich zugewiesen, in dem sie deklariert wurde. Diese „Aktion auf Distanz“ kann schwer nachvollziehbaren Code erzeugen, denn es ist nicht immer offensichtlich, wo einer Variable ein Wert zugewiesen wurde.

Advanced

Der Aufrufstapel lässt sich mit der Array-Variable FUNCNAME untersuchen.

Rückgabewerte

Eine Funktion hat, wie jeder Befehl, einen Exit-Status. Standardmäßig ist der Status einer Funktion der Exit-Status des zuletzt ausgeführten Befehls.

Mit dem Befehl return kannst du mit einem bestimmten Exit-Status aus einer Funktion zurückkehren.

check_password () {
    if [[ $1 == "secret" ]]; then
        return 0
    else
        return 1
    fi
}

read -sp "Enter your password: " pass

if check_password "$pass"; then
    echo "Correct!"
else
    echo "Wrong password."
fi

Verwendest du return ohne Argumente, wird der Status des zuletzt ausgeführten Befehls zurückgegeben.

Note

Beachte, dass sich die Funktion check_password so vereinfachen lässt:

check_password () { [[ $1 == "secret" ]]; }
  1. Das bedingte Konstrukt [[...]] hat einen Exit-Status: 0 für „wahr“, 1 für „falsch“.
  2. Das Gruppierungskonstrukt {...} braucht entweder einen Zeilenumbruch oder ein Semikolon vor der schließenden Klammer.

Ausgabe einer Funktion

Der Rückgabestatus einer Funktion ist nur eine Zahl. Wie kann eine Funktion eine Ausgabe erzeugen?

Deine Funktion kann auf die Standardausgabe schreiben. Mit der bekannten Kommandosubstitution fängst du sie ab:

d6 () { echo "$(( 1 + RANDOM % 6 ))"; }

die=$( d6 )
echo "You rolled a $die."

Ausgabe und Status gleichzeitig nutzen

Der Exit-Status einer Funktion steht dir auch dann zur Verfügung, wenn du die Ausgabe abfängst.

roll () {
    local n=$1
    if (( 4 <= n && n <= 20 )); then
        echo "$(( 1 + RANDOM % n ))"  # exit status is 0
    else
        return 1
    fi
}

read -p "How many faces does your die have? " faces
if die=$( roll "$faces" ); then
    echo "You rolled a $die."
else
    echo "I can't roll a die with $faces faces."
fi

Rekursion

Funktionen können sich selbst rekursiv aufrufen. Standardmäßig gibt es keine Begrenzung der Rekursionstiefe.

Ein Beispiel:

fibonacci() {
    local n=$1
    if (( n <= 1 )); then
        echo "1"
    else
        local a=$(fibonacci "$(( n - 1 ))")
        local b=$(fibonacci "$(( n - 2 ))")
        echo "$(( a + b ))"
    fi
}

for i in {1..10}; do fibonacci "$i"; done
# => 1
# => 2
# => 3
# => 5
# => 8
# => 13
# => 21
# => 34
# => 55
# => 89
Advanced

Die Rekursionstiefe lässt sich mit der Variable FUNCNEST steuern.

bash -c '
    recur() {
        echo $1
        recur $(($1 + 1))
    }
    FUNCNEST=5
    recur 1
'
1
2
3
4
5
environment: line 1: recur: maximum function nesting level exceeded (5)
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab