Zum Inhalt springen
Bash Anfänger 20 min

Funktionen in Bash

Bash-Funktionen definieren, Argumente verarbeiten, lokale Variablen, Rueckgabewerte per echo und Exit-Codes.

Aktualisiert:
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 Argumente
  • local fuer lokale Variablen (wichtig!)
  • return N fuer Exit-Code, echo fuer Ausgabe
  • $(funktion args) faengt die Ausgabe ein
  • Debug auf STDERR (>&2) trennt von Rueckgabe
  • source file.sh zum Einbinden von Libraries

Im naechsten Kapitel: Pipes, Redirects und die klassischen Unix-Tools.

Zurück zum Bash Kurs