Funktionen in Bash
Bash-Funktionen definieren, Argumente verarbeiten, lokale Variablen, Rueckgabewerte per echo und Exit-Codes.
Inhaltsverzeichnis
Funktionen in Bash
Funktionen strukturieren groessere Skripte. Bash-Funktionen sind schlanker als in anderen Sprachen - und haben ein paar Eigenheiten bei Argumenten und Rueckgaben.
Grundform
gruessen() {
echo "Hallo!"
}
gruessen
# Hallo!
Zwei Schreibweisen sind moeglich:
# Ohne "function"-Keyword
gruessen() { echo "Hallo!"; }
# Mit "function"-Keyword
function gruessen { echo "Hallo!"; }
Die erste Form ist portabler und gaengiger.
Argumente
Argumente sind nicht in der Signatur - du greifst auf $1, $2, … zu (wie bei Skripten):
gruessen() {
echo "Hallo, $1!"
}
gruessen "Anna"
# Hallo, Anna!
gruessen "Max" "Leo" "Tim" # nur $1 wird genutzt hier
Das funktioniert wie bei Skripten selbst:
demo() {
echo "Anzahl: $#"
echo "Alle: $*"
for arg in "$@"; do
echo " - $arg"
done
}
demo a b c
Lokale Variablen
Immer local in Funktionen:
addieren() {
local a=$1
local b=$2
local summe=$((a + b))
echo "$summe"
}
summe=5
addieren 3 4
echo "summe ist immer noch: $summe" # 5
Ohne local wuerde summe innerhalb der Funktion die globale Variable ueberschreiben.
”Rueckgabewerte”
Bash-Funktionen haben zwei Arten von Ergebnissen:
1. Exit-Code via return
istZahl() {
[[ "$1" =~ ^[0-9]+$ ]]
}
if istZahl "42"; then
echo "Ja"
fi
Oder explizit:
istZahl() {
if [[ "$1" =~ ^[0-9]+$ ]]; then
return 0 # Erfolg
else
return 1 # Fehler
fi
}
return liefert einen Exit-Code (0 = Erfolg). Das ist gut fuer Boolean-artige Funktionen.
2. Ausgabe via echo
Fuer echte Werte:
addieren() {
echo $(( $1 + $2 ))
}
ergebnis=$(addieren 3 4)
echo "$ergebnis" # 7
Du faengst die Ausgabe mit $(...) ein. Wichtig: Andere echo/printf-Aufrufe in der Funktion wuerden auch zum Ergebnis dazu kommen - also Ausgaben klein halten oder gezielt auf STDERR schicken.
Debug-Ausgaben auf STDERR
addieren() {
echo "Berechne $1 + $2..." >&2 # Debug auf STDERR
echo $(( $1 + $2 )) # eigentliches "Return" auf STDOUT
}
ergebnis=$(addieren 3 4) # nur "7" wird eingefangen
echo "$ergebnis" # 7
Mehrere “Rueckgabewerte”
Bash kann nur einen Exit-Code plus einen Output-Stream liefern. Fuer mehrere Werte:
Via Space-separierter Liste
min_max() {
local arr=("$@")
local min=${arr[0]}
local max=${arr[0]}
for z in "${arr[@]}"; do
(( z < min )) && min=$z
(( z > max )) && max=$z
done
echo "$min $max"
}
read min max < <(min_max 3 1 4 1 5 9 2)
echo "min=$min max=$max"
Via globale Variablen (Vorsicht)
berechne() {
RESULT_MIN=$1
RESULT_MAX=$2
}
Nicht schoen, aber manchmal pragmatisch.
Via Name-Referenz (Bash 4.3+)
min_max() {
local -n out_min=$1 # Name-Referenz
local -n out_max=$2
shift 2
out_min=$1
out_max=$1
for z in "$@"; do
(( z < out_min )) && out_min=$z
(( z > out_max )) && out_max=$z
done
}
min_max mn mx 3 1 4 1 5 9 2
echo "$mn $mx"
Das ist die moderne, saubere Loesung.
Argument-Parsing
Fuer einfache Skripte:
main() {
if [[ $# -lt 1 ]]; then
echo "Aufruf: $0 <name>" >&2
exit 1
fi
local name="$1"
echo "Hallo, $name!"
}
main "$@"
Mit Flags
verbose=0
datei=""
while [[ $# -gt 0 ]]; do
case "$1" in
-v|--verbose)
verbose=1
shift
;;
-f|--file)
datei="$2"
shift 2
;;
-h|--help)
echo "Hilfe: ..."
exit 0
;;
*)
echo "Unbekannt: $1" >&2
exit 1
;;
esac
done
Fuer groessere Skripte schau dir getopts (POSIX) oder argbash an.
Rekursion
fakultaet() {
if (( $1 <= 1 )); then
echo 1
return
fi
local vor=$(fakultaet $(( $1 - 1 )))
echo $(( $1 * vor ))
}
echo "$(fakultaet 5)" # 120
Funktioniert - aber Bash ist extrem langsam bei Rekursion. Fuer Mathematik lieber bc, awk oder eine echte Programmiersprache.
Funktionen in Bibliotheken
Du kannst Funktionen in separate Dateien auslagern:
lib.sh:
log_info() {
echo "[INFO] $*" >&2
}
log_error() {
echo "[ERROR] $*" >&2
}
script.sh:
#!/usr/bin/env bash
source /pfad/zu/lib.sh # oder: . /pfad/zu/lib.sh
log_info "Starte Skript"
log_error "Etwas schief gegangen"
source laedt den Inhalt in die aktuelle Shell - kein Sub-Prozess.
Funktionen exportieren
Wenn du Funktionen an Sub-Shells weitergeben willst:
meine_funktion() {
echo "Hallo"
}
export -f meine_funktion
bash -c 'meine_funktion' # funktioniert jetzt
Selten gebraucht, aber gut zu wissen.
Praktisches Beispiel
#!/usr/bin/env bash
set -euo pipefail
# Log-Helfer
log() {
local level="$1"; shift
local farbe="\033[0m"
case "$level" in
INFO) farbe="\033[32m" ;;
WARN) farbe="\033[33m" ;;
ERROR) farbe="\033[31m" ;;
esac
printf "%b[%s]\033[0m %s\n" "$farbe" "$level" "$*" >&2
}
log_info() { log "INFO" "$@"; }
log_warn() { log "WARN" "$@"; }
log_error() { log "ERROR" "$@"; }
# Pruefen, ob Kommando existiert
brauche() {
if ! command -v "$1" > /dev/null 2>&1; then
log_error "Benoetigt: $1. Bitte installieren."
exit 1
fi
}
# Deploy-Funktion
deploy() {
local umgebung="$1"
log_info "Deploying nach $umgebung..."
# ... echte Arbeit ...
log_info "Deploy erfolgreich"
}
main() {
brauche git
brauche docker
local umgebung="${1:-staging}"
deploy "$umgebung"
}
main "$@"
Das ist modernes, robustes Bash-Scripting.
Zusammenfassung
name() { ... }fuer Funktionen, keine Typen$1,$2, …,$@fuer Argumentelocalfuer lokale Variablen (wichtig!)return Nfuer Exit-Code,echofuer Ausgabe$(funktion args)faengt die Ausgabe ein- Debug auf STDERR (
>&2) trennt von Rueckgabe source file.shzum Einbinden von Libraries
Im naechsten Kapitel: Pipes, Redirects und die klassischen Unix-Tools.