Argumente sind die Brücke zwischen einem Skript und seinen Anwendern. Bash bietet dafür eine Reihe spezieller Variablen — von $1 bis ${10}, $@, $* und $# — sowie zwei Werkzeuge für strukturierte Optionen: das eingebaute getopts und das externe GNU getopt. Wer den Unterschied zwischen "$@" und "$*" kennt und getopts sauber einsetzt, vermeidet die häufigsten Skript-Bugs.

Was sind Parameter?

Parameter sind die Argumente, die du einem Skript oder einer Funktion beim Aufruf übergibst. Sie sind innerhalb des Codes über positions-basierte Variablen erreichbar und bilden die Schnittstelle zwischen Aufrufer und Skript-Logik.

Bash Skript mit Argumenten aufrufen
./backup.sh /home/user /mnt/backup --verbose

Innerhalb des Skripts hat jedes dieser drei Argumente eine eigene Position. Genau dieselben Mechanismen gelten für Bash-Funktionen: Auch hier liegen die übergebenen Werte in $1, $2, $3 und so weiter. Argumente sind in Bash immer Strings — eine Typisierung wie in anderen Sprachen gibt es nicht.

Positions-Parameter $1, $2, …

Die wichtigsten Variablen sind die positionalen Parameter. Sie geben dir Zugriff auf die einzelnen Argumente in der Reihenfolge ihrer Übergabe.

VariableBedeutung
$0Name des Skripts (bzw. der Shell bei interaktivem Aufruf)
$1Erstes Argument
$2Zweites Argument
$9Neuntes Argument
${10}Zehntes Argument — Klammern sind ab hier Pflicht
Bash Positionale Parameter ausgeben
#!/bin/bash
echo "Skript:    $0"
echo "Erstes:    $1"
echo "Zweites:   $2"
echo "Zehntes:   ${10}"
Output
Skript:    ./demo.sh
Erstes:    eins
Zweites:   zwei
Zehntes:   zehn

Eine klassische Falle: $10 wird nicht als zehntes Argument interpretiert, sondern als $1 gefolgt von der Ziffer 0. Wer ohne geschweifte Klammern auf zweistellige Positionen zugreift, bekommt also den Wert von $1 mit angehängter Null — ein Bug, der erst bei vielen Argumenten auffällt. Ab ${10} sind Klammern Pflicht.

$@ vs $*

Beide Variablen liefern alle Argumente — aber unterschiedlich. Der Unterschied wird erst sichtbar, wenn man sie quotet.

FormVerhaltenWann verwenden
$*Alle Args, ohne Quotes durch Wortzerlegung in Tokens aufgeteiltPraktisch nie
$@Alle Args, ohne Quotes wie $* (Wortzerlegung)Praktisch nie
"$*"Alle Args zu einem einzigen String, getrennt durch das erste Zeichen von IFSWenn man einen einzelnen String braucht
"$@"Alle Args als separate, korrekt gequotete TokensStandard — fast immer das, was du willst
Bash Unterschied zwischen $@ und $*
#!/bin/bash
# Aufruf: ./demo.sh "hallo welt" foo bar
for arg in "$@"; do
    echo "[at]  $arg"
done
for arg in "$*"; do
    echo "[star] $arg"
done
Output
[at]  hallo welt
[at]  foo
[at]  bar
[star] hallo welt foo bar

"$@" erhält die ursprüngliche Argument-Struktur — "hallo welt" bleibt ein Token. "$*" faltet alles zu einer einzigen Zeichenkette zusammen. Faustregel: Beim Weiterreichen von Argumenten an andere Befehle immer "$@" verwenden, sonst gehen Leerzeichen in Pfaden und Werten verloren.

$# — Anzahl der Argumente

$# enthält die Anzahl der übergebenen Argumente. Das ist die Grundlage für jede Validierung — ohne Argumente macht ein Skript meist keinen Sinn.

Bash Pflicht-Argumente prüfen
#!/bin/bash
if [ $# -lt 1 ]; then
    echo "Usage: $0 <quelle> [ziel]"
    exit 1
fi
echo "Anzahl Args: $#"
echo "Quelle:      $1"
Output
Anzahl Args: 2
Quelle:      /home/user

Das Muster if [ $# -lt N ]; then ... ; exit 1; fi ist eines der am häufigsten verwendeten Bash-Idiome überhaupt. Es prüft schon ganz oben im Skript, ob die nötigen Argumente da sind, und bricht mit einer Hilfemeldung ab, wenn nicht.

shift — Argumente weiterschieben

shift entfernt das erste Argument aus der Parameter-Liste. Alle folgenden Argumente rücken eine Position nach vorne: Aus $2 wird $1, aus $3 wird $2, und so weiter. $# verringert sich um eins.

Bash shift Schritt für Schritt
#!/bin/bash
# Aufruf: ./demo.sh a b c
echo "$1 $2 $3"
shift
echo "$1 $2"
shift 2
echo "verbleibend: $#"
Output
a b c
b c
verbleibend: 0

shift N schiebt gleich N Positionen weiter. Klassisch wird shift in einer Schleife verwendet, um Argumente nacheinander zu verarbeiten — siehe Praxis-Patterns weiter unten.

Default-Werte und Pflichtprüfung

Die Parameter-Expansion erlaubt es, Default-Werte oder Fehler direkt bei der Verwendung zu setzen — ohne separates if.

Bash Default- und Required-Werte
#!/bin/bash
name="${1:-Welt}"
ziel="${2:?Zielpfad fehlt}"
echo "Hallo, $name!"
echo "Schreibe nach: $ziel"
Output
Hallo, Welt!
Schreibe nach: /tmp/out

${1:-Welt} liefert $1, falls gesetzt und nicht leer — sonst den Default Welt. ${2:?Zielpfad fehlt} bricht das Skript mit der Fehlermeldung ab, wenn $2 leer oder nicht gesetzt ist. Eine ausführliche Übersicht aller Operatoren findest du im Artikel zur String-Manipulation.

getopts — Flag-Parsing (POSIX)

Sobald ein Skript mehr als ein paar Argumente erwartet, wird manuelles Parsen unübersichtlich. getopts ist ein Bash-Builtin, das Single-Character-Flags wie -v, -h oder -f datei sauber verarbeitet — POSIX-konform und in jeder Shell verfügbar.

Der OPTSTRING beschreibt die erlaubten Flags. Ein nachgestellter Doppelpunkt bedeutet: dieses Flag erwartet ein Argument. Das geparste Flag landet in der angegebenen Variable, ein eventuelles Argument in $OPTARG.

Bash getopts-Schleife
#!/bin/bash
verbose=0
file=""

while getopts "vhf:" opt; do
    case $opt in
        v) verbose=1 ;;
        h) echo "Usage: $0 [-v] [-h] [-f datei]"; exit 0 ;;
        f) file="$OPTARG" ;;
        \?) echo "Unbekanntes Flag: -$OPTARG" >&2; exit 1 ;;
    esac
done
shift $((OPTIND - 1))

echo "verbose=$verbose file=$file rest=$*"
Output
verbose=1 file=daten.txt rest=eingabe1 eingabe2

Nach der Schleife sorgt shift $((OPTIND - 1)) dafür, dass die geparsten Flags entfernt werden — übrig bleiben in $@ nur noch die nicht-flag Argumente. Das ist essenziell, wenn anschließend Positions-Argumente verarbeitet werden.

Die zentrale Limitation: getopts kennt keine Long-Options. --verbose oder --file=daten.txt werden nicht unterstützt. Wer das braucht, greift zu GNU getopt.

GNU getopt — Long-Options

getopt (ohne s) ist ein externes Programm, kein Builtin. Die GNU-Variante unterstützt sowohl Short- als auch Long-Options. Beachte den Namensunterschied: getopts (Builtin, nur Short) vs. getopt (externes Tool, Long-Options möglich — aber nur in der GNU-Variante).

VariantePlattformLong-OptionsEmpfehlung
GNU getoptLinux, util-linuxJaStandard für ernsthafte Skripte
BSD getoptmacOS, FreeBSDNeinPraktisch unbrauchbar
getopts (Builtin)Bash, dash, kshNeinFür POSIX-Skripte ohne Long-Options

Auf macOS ist standardmäßig die BSD-Variante installiert, die keine Long-Options kennt. Abhilfe schafft brew install gnu-getopt — danach liegt die GNU-Variante als /opt/homebrew/opt/gnu-getopt/bin/getopt.

Bash GNU getopt mit Long-Options
#!/bin/bash
PARSED=$(getopt -o vhf: --long verbose,help,file: -n "$0" -- "$@") || exit 1
eval set -- "$PARSED"

verbose=0
file=""
while true; do
    case "$1" in
        -v|--verbose) verbose=1; shift ;;
        -h|--help)    echo "Hilfe..."; exit 0 ;;
        -f|--file)    file="$2"; shift 2 ;;
        --)           shift; break ;;
    esac
done

echo "verbose=$verbose file=$file rest=$*"
Output
verbose=1 file=daten.txt rest=eingabe1

Der Trick mit eval set -- "$PARSED" setzt die Positions-Parameter neu — diesmal sauber normalisiert von getopt. Das -- markiert das Ende der Optionen; alles danach sind reguläre Argumente.

Praxis-Patterns

Die folgenden Muster begegnen dir in praktisch jedem produktiven Bash-Skript. Sie sind klein, robust und seit Jahrzehnten bewährt.

Pflicht-Argument-Check

Bash Argument vorhanden? Sonst Usage und exit
[ $# -lt 1 ] && { echo "Usage: $0 <pfad>"; exit 1; }

Einzeiler ganz oben im Skript: Fehlt das erste Argument, gibt das Skript eine Usage-Zeile aus und beendet sich mit Exit-Code 1. Die Klammern um echo/exit gruppieren beide Befehle, sodass && nur dann ausgelöst wird, wenn die Bedingung wahr ist.

Vollständiger getopts-Loop

Bash getopts mit -v, -h, -f file
verbose=0
file=""
while getopts "vhf:" opt; do
    case $opt in
        v) verbose=1 ;;
        h) echo "Usage: $0 [-v] [-f file] args..."; exit 0 ;;
        f) file="$OPTARG" ;;
        *) exit 1 ;;
    esac
done
shift $((OPTIND - 1))

Der Standard-Baustein für Bash-Skripte mit Short-Flags. OPTIND ist die Position des nächsten zu prüfenden Arguments — das shift danach entfernt alle bereits verarbeiteten Flags.

shift-Loop für freies Argument-Parsing

Bash Manuelles Parsing mit shift
while [ $# -gt 0 ]; do
    case $1 in
        --verbose) verbose=1 ;;
        --file)    file="$2"; shift ;;
        *)         echo "Unbekannt: $1" >&2; exit 1 ;;
    esac
    shift
done

Wenn weder getopts noch getopt zur Verfügung stehen, oder wenn man ungewöhnliche Argument-Formen unterstützen will: Schleife über $#, im case jedes erwartete Flag behandeln, am Ende einmal shift. Bei Flags mit Wert ein zusätzliches shift im Case-Zweig.

Default-Werte für Funktions-Parameter

Bash Default-Wert beim Aufruf
greet() {
    local name="${1:-Welt}"
    echo "Hallo, $name!"
}
greet           # Hallo, Welt!
greet "Anna"    # Hallo, Anna!

Funktionen haben in Bash keine Parameter-Defaults wie in anderen Sprachen — aber ${1:-Welt} löst das Problem in einer Zeile. local sorgt dafür, dass die Variable nur in der Funktion existiert.

Catch-all: Argumente weiterreichen

Bash Wrapper-Skript
#!/bin/bash
export LANG=C
exec mein-tool "$@"

Ein Wrapper-Skript, das nur die Umgebung anpasst und alle Argumente unverändert an das eigentliche Tool durchreicht. Pflicht: "$@" mit Quotes — sonst werden Argumente mit Leerzeichen zerlegt. exec ersetzt den Skript-Prozess durch das Ziel-Programm und spart einen Shell-Prozess.

Häufige Stolperfallen

Die $10-Falle

Ohne geschweifte Klammern interpretiert Bash $10 als $1 gefolgt von einer literalen 0. Bei einem Aufruf mit zehn Argumenten gibt echo $10 also nicht das zehnte Argument aus, sondern den Wert von $1 mit angehängter Null — zum Beispiel eins0. Korrekt ist immer ${10}. Ab der zweistelligen Position sind Klammern Pflicht. Der Bug ist tückisch, weil er bei kurzen Aufrufen nie auftritt und erst in der Produktion zuschlägt.

Unquoted $@ zerstört Argumente mit Leerzeichen

Schreibst du cp $@ /ziel/, zerlegt die Shell jedes Argument an Leerzeichen. Aus dem Pfad /home/user/Mein Projekt werden zwei Argumente: /home/user/Mein und Projekt/. Korrekt ist immer cp "$@" /ziel/ — die Quotes sorgen dafür, dass jedes Argument als einzelnes Token erhalten bleibt. Diese Regel gilt auch für Variablen, die Pfade enthalten: "$datei", nicht $datei. Es ist wahrscheinlich der häufigste Bug in fehlerhaften Bash-Skripten überhaupt.

BSD getopt kann keine Long-Options

Auf macOS und FreeBSD ist standardmäßig die BSD-Variante von getopt installiert. Sie unterstützt keine Long-Options wie --verbose und keine optionalen Argumente. Skripte, die mit GNU getopt entwickelt wurden, scheitern auf macOS still oder mit kryptischen Fehlern. Abhilfe: brew install gnu-getopt und das resultierende Binary explizit per Pfad aufrufen, oder direkt auf das Bash-Builtin getopts (mit s) ausweichen, das überall identisch funktioniert — allerdings ohne Long-Options.

OPTIND nicht zurücksetzen bei wiederholtem getopts-Aufruf

getopts verwendet die globale Variable OPTIND, um sich die aktuelle Position zu merken. Wenn du getopts in einer Funktion verwendest, die mehrfach aufgerufen wird, startet der zweite Aufruf nicht bei Position 1, sondern dort, wo der erste aufgehört hat — die Argumente werden ignoriert. Lösung: Innerhalb der Funktion mit local OPTIND=1 (oder am Anfang OPTIND=1) den Zähler explizit zurücksetzen. Sonst bekommst du Bugs, die nur beim zweiten Aufruf auftreten.

Fehlendes Doppelpunkt-Zeichen im OPTSTRING

Im OPTSTRING von getopts markiert ein nachgestellter : ein Flag, das ein Argument erwartet. Schreibst du getopts "vhf" opt statt getopts "vhf:" opt, wird -f als reines Schalter-Flag behandelt — der nachfolgende Dateiname landet als nächstes positionales Argument im falschen Topf. Schlimmer noch: Es gibt keine Fehlermeldung, das Skript läuft scheinbar normal weiter, aber $OPTARG bleibt leer. Beim Schreiben des OPTSTRING immer prüfen, welche Flags ein Argument brauchen.

Trailing -- als Optionen-Separator

Das doppelte Minus -- ist das klassische POSIX-Idiom, um das Ende der Optionen zu markieren. Alles danach behandelt das Skript als reguläres Argument, selbst wenn es mit einem Minus beginnt. Beispiel: rm -- -datei.txt löscht eine Datei, die -datei.txt heißt — ohne -- würde rm versuchen, -datei.txt als Flag zu interpretieren. In eigenen Skripten solltest du das Idiom mit getopt oder einem expliziten case ebenfalls unterstützen, vor allem wenn Dateinamen oder beliebige Werte verarbeitet werden.

Weiterführende Ressourcen

Externe Quellen

  • Skript-Grundlagen — Aufbau eines Bash-Skripts, Shebang und Ausführung
  • Funktionen — Eigene Funktionen definieren und parametrisieren
  • Bedingungenif, case und Test-Ausdrücke für Argument-Validierung
  • String-Manipulation — Parameter-Expansion mit ${var:-default}, ${var:?fehler} und mehr
  • Exit-Codes — Saubere Rückgabewerte für Fehler bei fehlenden Argumenten
/ Weiter

Zurück zu Shell-Scripting

Zur Übersicht