Zum Inhalt springen

Bash-Spickzettel

Syntax zum Nachschlagen, ohne Erklärungen. Die Herleitung findest du in Modul 7 und Modul 8. Alle Beispiele setzen Bash 5 voraus und sind ShellCheck-sauber.

Variable Bedeutung
$0 Name des Skripts (so wie es aufgerufen wurde)
$1 $2 … $9 ${10} Positionsparameter, ab 10 mit geschweiften Klammern
$# Anzahl der Positionsparameter
"$@" Alle Parameter, jeder einzeln gequotet. Fast immer das Richtige
"$*" Alle Parameter als ein Wort, getrennt durch das erste Zeichen von IFS
$? Exit-Status des letzten Befehls (0 = Erfolg)
$$ PID der aktuellen Shell
$! PID des zuletzt gestarteten Hintergrundprozesses
$_ Letztes Argument des vorherigen Befehls
$- Aktive Shell-Optionen, zum Beispiel himBHs
$IFS Feldtrenner, Standard: Leerzeichen, Tab, Zeilenumbruch
$LINENO Aktuelle Zeilennummer im Skript
$RANDOM Zufallszahl 0 bis 32767
$SECONDS Sekunden seit Start der Shell
$HOME $USER $PATH $PWD $SHELL Umgebung
shift $2 wird $1, $# sinkt um eins (shift 2 für zwei)

In Bash-Skripten [[ ]] verwenden (keine Wortaufteilung, && und || erlaubt, Muster möglich). [ ] ist das POSIX-Builtin test.

Dateien

Test Wahr, wenn
[[ -e pfad ]] existiert (beliebiger Typ)
[[ -f pfad ]] reguläre Datei
[[ -d pfad ]] Verzeichnis
[[ -L pfad ]] symbolischer Link
[[ -r pfad ]] -w -x lesbar, schreibbar, ausführbar für dich
[[ -s pfad ]] existiert und ist nicht leer
[[ a -nt b ]] / -ot a neuer als b / älter als b

Strings

Test Wahr, wenn
[[ -z "$s" ]] leer
[[ -n "$s" ]] nicht leer
[[ "$a" == "$b" ]] gleich
[[ "$a" != "$b" ]] ungleich
[[ "$a" == abc* ]] passt auf Glob-Muster (rechte Seite ungequotet)
[[ "$a" =~ ^[0-9]+$ ]] passt auf regulären Ausdruck, Gruppen in BASH_REMATCH
[[ "$a" < "$b" ]] alphabetisch davor

Zahlen

Test Bedeutung
[[ "$a" -eq "$b" ]] gleich
[[ "$a" -ne "$b" ]] ungleich
[[ "$a" -lt "$b" ]] / -le kleiner / kleiner gleich
[[ "$a" -gt "$b" ]] / -ge grösser / grösser gleich
(( a < b )) Arithmetischer Vergleich, lesbarer, kein $ nötig

Verknüpfen

Terminal-Fenster
[[ -f "$datei" && -r "$datei" ]]
[[ "$n" -lt 1 || "$n" -gt 10 ]]
[[ ! -d "$dir" ]]
Ausdruck Ergebnis
${var} Wert von var, Klammern trennen vom Folgetext: ${var}_backup
${var:-wert} var falls gesetzt und nicht leer, sonst wert (ohne Zuweisung)
${var:=wert} Wie oben, weist wert zusätzlich zu
${var:?meldung} Bricht mit meldung ab, wenn var leer ist
${var:+wert} wert, falls var gesetzt ist, sonst leer
${#var} Länge des Strings
${var#muster} Kürzestes muster am Anfang entfernen: ${pfad#*/}
${var##muster} Längstes muster am Anfang entfernen: ${pfad##*/} ergibt den Dateinamen
${var%muster} Kürzestes muster am Ende entfernen: ${datei%.*} ergibt den Namen ohne Endung
${var%%muster} Längstes muster am Ende entfernen: ${pfad%%/*}
${var/alt/neu} Erstes Vorkommen ersetzen
${var//alt/neu} Alle Vorkommen ersetzen
${var^} / ${var^^} Erster Buchstabe / alle Buchstaben gross
${var,} / ${var,,} Erster Buchstabe / alle Buchstaben klein
${var:2:3} Teilstring ab Position 2 (nullbasiert), 3 Zeichen lang
${var: -3} Letzte drei Zeichen (Leerzeichen vor dem Minus ist Pflicht)
${!var} Indirekt: Wert der Variablen, deren Name in var steht
${!prefix*} Namen aller Variablen, die mit prefix beginnen
Terminal-Fenster
pfad="/home/alice/bericht.final.txt"
echo "${pfad##*/}" # bericht.final.txt
echo "${pfad%/*}" # /home/alice
echo "${pfad%.*}" # /home/alice/bericht.final
echo "${pfad##*.}" # txt
Terminal-Fenster
zaehler=$(( zaehler + 1 )) # sicher mit set -e, siehe unten
(( summe += wert ))
ergebnis=$(( (a + b) * 2 ))
rest=$(( a % b ))
if (( a > b )); then echo "a ist grösser"; fi
for (( i = 0; i < 5; i++ )); do echo "$i"; done

Nur Ganzzahlen. Operatoren: + - * / % **, Vergleiche < <= > >= == !=, logisch && || !. Für Kommazahlen bc: echo "scale=2; 10/3" | bc.

Terminal-Fenster
farben=(rot gruen blau) # anlegen
farben+=(gelb) # anhängen
echo "${farben[0]}" # erstes Element
echo "${farben[-1]}" # letztes Element
echo "${farben[@]}" # alle Elemente, einzeln gequotet
echo "${#farben[@]}" # Anzahl
echo "${!farben[@]}" # Indizes
unset 'farben[1]' # Element entfernen
for f in "${farben[@]}"; do echo "$f"; done
declare -A alter # assoziatives Array (Hash)
alter[alice]=30
alter[bob]=25
echo "${alter[alice]}"
for name in "${!alter[@]}"; do echo "$name: ${alter[$name]}"; done
mapfile -t zeilen < datei.txt # Datei zeilenweise in Array lesen
Terminal-Fenster
# if / elif / else
if [[ bedingung ]]; then
befehle
elif [[ bedingung ]]; then
befehle
else
befehle
fi
# case
case "$wahl" in
1|a) befehle ;;
[Jj]*) befehle ;;
*) echo "unbekannt" ;;
esac
# for über Liste
for datei in *.txt; do
echo "$datei"
done
# for über Zahlenbereich
for i in {1..10}; do echo "$i"; done
for i in $(seq 1 2 10); do echo "$i"; done
# while
while [[ "$n" -gt 0 ]]; do
(( n-- ))
done
# until
until ping -c 1 -W 1 "$host" &>/dev/null; do sleep 1; done
# Datei zeilenweise lesen
while IFS= read -r zeile; do
echo "$zeile"
done < datei.txt
# Endlosschleife mit Abbruch
while true; do
read -rp "Eingabe (q beendet): " eingabe
[[ "$eingabe" == q ]] && break
done
# break verlässt die Schleife, continue springt zum nächsten Durchlauf

Kurzformen: befehl && bei_erfolg, befehl || bei_fehler, befehl || exit 1.

Terminal-Fenster
begruesse_benutzer() {
local name="${1:-Welt}" # Parameter mit Standardwert
local anzahl=$#
echo "Hallo, $name ($anzahl Argumente)"
return 0 # Exit-Status 0 bis 255, nicht für Daten
}
hole_datum() {
date +%Y-%m-%d # Rückgabe von Daten über stdout
}
begruesse_benutzer "Alice"
heute=$(hole_datum)

Variablen in Funktionen immer mit local. Funktionen müssen vor dem ersten Aufruf definiert sein.

Terminal-Fenster
usage() {
echo "Benutzung: $0 [-h] [-v] [-n NAME] datei..." >&2
exit 1
}
verbose=0
name=""
while getopts "hvn:" opt; do
case "$opt" in
h) usage ;;
v) verbose=1 ;;
n) name="$OPTARG" ;;
*) usage ;;
esac
done
shift $(( OPTIND - 1 )) # danach sind "$@" die restlichen Argumente
[[ $# -ge 1 ]] || usage

Ein Doppelpunkt hinter dem Buchstaben (n:) heisst: diese Option braucht einen Wert. Lange Optionen (--name) kann getopts nicht, dafür eine while-Schleife mit case "$1".

Syntax Wirkung
befehl > datei stdout in Datei (überschreiben)
befehl >> datei stdout anhängen
befehl 2> datei stderr in Datei
befehl 2>&1 stderr dorthin, wo stdout gerade hingeht
befehl > datei 2>&1 beides in Datei (Reihenfolge wichtig)
befehl &> datei Kurzform für beides (Bash)
befehl 2>/dev/null Fehler verwerfen
befehl < datei stdin aus Datei
a | b stdout von a wird stdin von b
a |& b stdout und stderr von a nach b
befehl | tee datei Ausgabe auf Bildschirm und in Datei
befehl <<< "text" Here-String: Text als stdin
diff <(ls a) <(ls b) Prozess-Substitution: Ausgabe als Datei
exec > logdatei 2>&1 Ab hier alle Ausgaben des Skripts in Datei
Terminal-Fenster
cat <<'EOT' > "$ziel"
Text wird wörtlich übernommen, $variablen bleiben stehen.
EOT
cat <<EOT
Hallo $USER, heute ist $(date +%A).
EOT
cat <<-EOT
Führende Tabs (keine Leerzeichen) werden entfernt.
EOT

Begrenzer in Anführungszeichen ('EOT') verhindert Expansion. <<- erlaubt Einrückung mit Tabs.

Option Wirkung
set -e Skript bricht beim ersten fehlgeschlagenen Befehl ab (errexit)
set -u Nicht gesetzte Variablen sind ein Fehler (nounset)
set -o pipefail Pipeline scheitert, wenn ein Glied scheitert, nicht nur das letzte
set -euo pipefail Die Kombination für jedes Skript im Kurs
set -x Jeden Befehl vor der Ausführung anzeigen (Debugging), set +x beendet
set -n Nur Syntax prüfen, nichts ausführen (bash -n skript.sh)
shopt -s nullglob Muster ohne Treffer ergibt leere Liste statt dem Muster selbst
shopt -s globstar ** passt rekursiv auf Verzeichnisse

Ausnahmen von -e: Befehle in if, while, &&, || und ! dürfen scheitern. Einen erwarteten Fehler fängst du so ab: grep -q muster datei || true. Falle: (( i++ )) hat den Exit-Status 1, wenn das Ergebnis 0 ist, und beendet mit set -e das Skript. Darum i=$(( i + 1 )) schreiben.

Terminal-Fenster
tmpdir=$(mktemp -d)
cleanup() {
rm -rf "$tmpdir"
}
trap cleanup EXIT # bei jedem Ende, auch bei Fehler oder Ctrl+C
trap 'echo "Abbruch"; exit 130' INT
trap 'echo "Fehler in Zeile $LINENO" >&2' ERR

Wichtige Signale: EXIT (Skriptende), INT (Ctrl+C), TERM (kill), HUP (Terminal geschlossen), ERR (Befehl scheitert, mit set -e).

ShellCheck prüft Skripte: shellcheck skript.sh. Jeder Code hat eine Wiki-Seite: https://www.shellcheck.net/wiki/SC2086.

Code Meldung (gekürzt) Bedeutung und Lösung
SC2086 Double quote to prevent globbing and word splitting Variable ungequotet. "$var" statt $var. Der häufigste Fehler überhaupt
SC2046 Quote this to prevent word splitting Ungequotete Befehlssubstitution. "$(befehl)"
SC2006 Use $(...) notation instead of legacy backticks Backticks sind veraltet, $(...) ist lesbar und schachtelbar
SC2155 Declare and assign separately to avoid masking return values local x=$(cmd) verschluckt den Exit-Status. Erst local x, dann x=$(cmd)
SC2162 read without -r will mangle backslashes read -r verwenden
SC2164 Use cd ... || exit in case cd fails cd "$dir" || exit 1, sonst läuft das Skript im falschen Verzeichnis weiter
SC2034 Variable appears unused Tippfehler oder Variable nie verwendet
SC2068 Double quote array expansions "${arr[@]}" statt ${arr[@]}
SC2115 Use "${var:?}" to ensure this never expands to / Schutz vor rm -rf "$dir/" bei leerem $dir
SC2148 Tips depend on target shell, please add a shebang Shebang fehlt
SC2181 Check exit code directly if befehl; then statt befehl; if [[ $? -eq 0 ]]
SC1090/SC1091 Can’t follow non-constant source Hinweis bei source "$datei", meist harmlos
SC2002 Useless cat grep muster datei statt cat datei | grep muster

Eine Warnung gezielt ausschalten, wenn sie in einem Fall falsch ist:

Terminal-Fenster
# shellcheck disable=SC2034
farbe_rot='\033[31m'