Bash — Shell-Skripting und interaktive Kommandozeile
Praxis-Guide zu Bash: Variablen, Parameter-Expansion, Schleifen, Funktionen, Umleitungen und robuste Skripte mit set -euo pipefail.
Bash ist die Standard-Shell der meisten Linux-Distributionen und zugleich eine vollwertige Skriptsprache. Ob du interaktiv auf der Kommandozeile arbeitest oder Automatisierungs-Skripte schreibst – Variablen, Parameter-Expansion, Schleifen und Funktionen sind dein täglich Brot. Dieser Guide bündelt die Bausteine, die du wirklich brauchst: von String-Manipulation über Umleitungen und Prozesssteuerung bis zu robusten Skript-Patterns. Behalte dabei den Unterschied zwischen interaktiver und Skript-Nutzung im Hinterkopf – beide folgen denselben Regeln, stellen aber andere Ansprüche.
Variablen & Strings
VAR='value' — Weist einer Variablen einen Wert zu. Keine Leerzeichen ums Gleichheitszeichen.
NAME='World'echo "$VAR" — Expandiert die Variable in doppelten Anführungszeichen. Einfache Anführungszeichen verhindern die Expansion.
echo "Hello $NAME"echo "${VAR}text" — Klammern grenzen den Variablennamen vom umgebenden Text ab.
echo "${NAME}_backup"readonly VAR='value' — Deklariert eine schreibgeschützte (konstante) Variable.
readonly PI='3.14159'unset VAR — Entfernt eine Variable.
unset TEMP_FILEexport VAR='value' — Exportiert die Variable an Kindprozesse (Umgebungsvariable).
export PATH="$HOME/bin:$PATH"local VAR='value' — Deklariert eine lokale Variable innerhalb einer Funktion.
local count=0Parameter-Expansion
${VAR:-default} — Nutzt den Default-Wert, wenn die Variable nicht gesetzt oder leer ist.
echo "${USER:-anonymous}"${VAR:=default} — Weist den Default-Wert zu, wenn die Variable nicht gesetzt oder leer ist.
echo "${LANG:=en_US.UTF-8}"${VAR:+alternate} — Nutzt den Alternativwert, wenn die Variable gesetzt und nicht leer ist.
echo "${DEBUG:+--verbose}"${VAR:?error message} — Gibt eine Fehlermeldung aus und bricht ab, wenn die Variable nicht gesetzt oder leer ist.
${API_KEY:?API_KEY must be set}${#VAR} — Ermittelt die Länge einer String-Variablen.
echo "Length: ${#NAME}"${VAR^^} / ${VAR,,} — Wandelt in Groß- / Kleinbuchstaben um (Bash 4+).
echo "${name^^}" # HELLOString-Manipulation
${VAR#pattern} — Entfernt die kürzeste Übereinstimmung des Musters am Anfang.
FILE='archive.tar.gz'; echo "${FILE#*.}" # tar.gz${VAR##pattern} — Entfernt die längste Übereinstimmung des Musters am Anfang.
PATH='/home/user/file.txt'; echo "${PATH##*/}" # file.txt${VAR%pattern} — Entfernt die kürzeste Übereinstimmung des Musters am Ende.
FILE='photo.jpg'; echo "${FILE%.*}" # photo${VAR%%pattern} — Entfernt die längste Übereinstimmung des Musters am Ende.
FILE='backup.tar.gz'; echo "${FILE%%.*}" # backup${VAR/pattern/replacement} — Ersetzt das erste Vorkommen des Musters.
echo "${STR/foo/bar}"${VAR//pattern/replacement} — Ersetzt alle Vorkommen des Musters.
echo "${PATH//:/ }"${VAR:offset:length} — Extrahiert eine Teilzeichenkette ab Offset mit angegebener Länge.
STR='Hello World'; echo "${STR:6:5}" # WorldArrays
ARR=(val1 val2 val3) — Deklariert ein indiziertes Array.
COLORS=(red green blue)echo "${ARR[0]}" — Greift per Index auf ein Array-Element zu (0-basiert).
echo "${COLORS[1]}" # greenecho "${ARR[@]}" — Expandiert alle Array-Elemente.
echo "${COLORS[@]}"echo "${#ARR[@]}" — Ermittelt die Anzahl der Elemente eines Arrays.
echo "${#COLORS[@]}" # 3ARR+=(val4) — Hängt ein Element an ein Array an.
COLORS+=(yellow)declare -A MAP — Deklariert ein assoziatives Array (Bash 4+).
declare -A PORTS; PORTS[http]=80; PORTS[https]=443for item in "${ARR[@]}"; do ... done — Iteriert über alle Array-Elemente.
for c in "${COLORS[@]}"; do echo "$c"; doneBedingungen
if [[ condition ]]; then ... fi — Einfaches if-Statement mit erweiterten Test-Klammern.
if [[ -f 'config.txt' ]]; then echo 'Found'; fiif [[ cond ]]; then ... else ... fi — If-else-Statement.
if [[ $# -gt 0 ]]; then echo "$1"; else echo 'No args'; fi[[ -f <file> ]] — Prüft, ob eine Datei existiert und eine reguläre Datei ist.
[[ -f /etc/hosts ]] && echo 'Exists'[[ -d <dir> ]] — Prüft, ob ein Verzeichnis existiert.
[[ -d /tmp ]] && echo 'Directory exists'[[ -z "$VAR" ]] — Prüft, ob ein String leer ist (Länge null).
[[ -z "$INPUT" ]] && echo 'Empty input'[[ -n "$VAR" ]] — Prüft, ob ein String nicht leer ist.
[[ -n "$USER" ]] && echo "User: $USER"[[ "$A" == "$B" ]] — Vergleicht zwei Strings auf Gleichheit.
[[ "$answer" == 'yes' ]] && proceed[[ "$A" =~ regex ]] — Regex-Test (Extended Regular Expressions).
[[ "$email" =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]] && echo 'Valid'Schleifen
for VAR in list; do ... done — Iteriert über eine Liste von Elementen.
for f in *.txt; do echo "$f"; donefor (( i=0; i<n; i++ )); do ... done — C-artige for-Schleife mit Zähler.
for (( i=1; i<=5; i++ )); do echo "$i"; donefor VAR in {start..end}; do ... done — Iteriert über einen Zahlenbereich per Brace-Expansion.
for i in {1..10}; do echo "$i"; donewhile [[ condition ]]; do ... done — Schleife, solange die Bedingung wahr ist.
while [[ $count -lt 10 ]]; do ((count++)); donewhile read -r line; do ... done < <file> — Liest eine Datei zeilenweise.
while read -r line; do echo "$line"; done < data.txt<command> | while read -r line; do ... done — Verarbeitet die Ausgabe eines Befehls zeilenweise.
find . -name '*.log' | while read -r f; do wc -l "$f"; doneuntil [[ condition ]]; do ... done — Schleife, bis die Bedingung wahr wird.
until [[ -f /tmp/ready ]]; do sleep 1; doneFunktionen
fname() { ... } — Definiert eine Funktion. Bevorzugte Syntax gegenüber dem function-Schlüsselwort.
greet() { echo "Hello, $1"; }$1, $2, ..., $@, $# — Greift auf Funktionsargumente zu. $@ sind alle Argumente, $# ist deren Anzahl.
add() { echo $(( $1 + $2 )); }; add 3 5return <n> — Gibt einen Exit-Status aus einer Funktion zurück (0-255). Für Werte echo verwenden.
is_root() { [[ $EUID -eq 0 ]] && return 0 || return 1; }result=$(fname arg) — Fängt die Funktionsausgabe per Command-Substitution in einer Variablen ein.
upper() { echo "${1^^}"; }; NAME=$(upper 'hello')Arithmetik
$(( expression )) — Arithmetische Expansion. Unterstützt +, -, *, /, %, **.
echo "$(( 5 + 3 ))" # 8(( VAR++ )) / (( VAR-- )) — Inkrementiert / dekrementiert eine Variable.
count=0; (( count++ )); echo "$count" # 1(( VAR += n )) — Zusammengesetzte Zuweisungsoperatoren (+=, -=, *=, /=, %=).
total=100; (( total -= 25 )); echo "$total" # 75$(( RANDOM % n )) — Erzeugt eine Zufallszahl zwischen 0 und n-1.
echo "Dice: $(( RANDOM % 6 + 1 ))"Umleitung & Pipes
cmd > file — Leitet stdout in eine Datei um (überschreibt).
echo 'hello' > output.txtcmd >> file — Leitet stdout in eine Datei um (hängt an).
echo 'line' >> log.txtcmd 2> file — Leitet stderr in eine Datei um.
find / -name '*.conf' 2> /dev/nullcmd &> file — Leitet stdout und stderr in eine Datei um.
make &> build.logcmd 2>&1 — Leitet stderr nach stdout um (kombiniert die Streams).
command 2>&1 | tee output.logcmd1 | cmd2 — Leitet stdout von cmd1 an stdin von cmd2 weiter (Pipe).
ps aux | grep nginxcmd1 |& cmd2 — Leitet stdout und stderr an cmd2 weiter.
make |& lesscmd <<< 'string' — Here-String. Übergibt einen String als stdin an einen Befehl.
grep 'error' <<< "$log_output"Prozesssteuerung
cmd & — Führt einen Befehl im Hintergrund aus.
sleep 60 &jobs — Listet Hintergrund-Jobs der aktuellen Shell auf.
jobs -lfg %<n> — Holt Hintergrund-Job Nummer n in den Vordergrund.
fg %1bg %<n> — Setzt einen gestoppten Job im Hintergrund fort.
bg %1wait [pid] — Wartet auf das Ende eines Hintergrundprozesses. Ohne Argument auf alle.
cmd1 & cmd2 &; wait$! — PID des zuletzt gestarteten Hintergrundprozesses.
server & echo "PID: $!"$$ — PID der aktuellen Shell.
echo "Script PID: $$"trap 'cmd' SIGNAL — Führt einen Befehl aus, wenn ein Signal empfangen wird.
trap 'rm -f /tmp/lockfile; exit' EXIT INT TERMBefehlsverkettung
cmd1 && cmd2 — Führt cmd2 nur aus, wenn cmd1 erfolgreich war (Exit-Code 0).
mkdir build && cd buildcmd1 || cmd2 — Führt cmd2 nur aus, wenn cmd1 fehlschlägt (Exit-Code ungleich 0).
grep -q 'error' log.txt || echo 'No errors'cmd1; cmd2 — Führt cmd2 nach cmd1 aus, unabhängig vom Exit-Status.
echo 'Start'; date; echo 'End'{ cmd1; cmd2; } — Gruppiert Befehle in der aktuellen Shell (Semikolon und Leerzeichen beachten).
{ echo 'Header'; cat data.txt; } > report.txt(cmd1; cmd2) — Führt Befehle in einer Subshell aus (ohne Einfluss auf die Eltern-Shell).
(cd /tmp && tar xzf archive.tar.gz)Spezielle Variablen
$? — Exit-Status des letzten Befehls (0 = Erfolg).
grep -q 'root' /etc/passwd; echo "$?"$0 — Name des aktuellen Skripts oder der Shell.
echo "Script: $0"$# / $@ / $* — Anzahl der Argumente / alle Argumente (als einzelne Wörter / als ein Wort).
echo "$# arguments: $@"$_ — Letztes Argument des vorigen Befehls.
mkdir mydir; cd "$_"$RANDOM — Zufällige Ganzzahl zwischen 0 und 32767.
echo "$RANDOM"$SECONDS — Sekunden seit dem Start der Shell. Lässt sich zurücksetzen.
SECONDS=0; sleep 2; echo "Elapsed: ${SECONDS}s"$LINENO — Aktuelle Zeilennummer im Skript.
echo "Error at line $LINENO"Tastenkürzel
Ctrl+R — Rückwärtssuche in der Befehls-History.
Ctrl+R, then type part of a previous commandCtrl+A / Ctrl+E — Bewegt den Cursor an den Anfang / das Ende der Zeile.
Ctrl+A to jump to startCtrl+W / Alt+D — Löscht das Wort vor / nach dem Cursor.
Ctrl+W to delete previous wordCtrl+U / Ctrl+K — Löscht von Cursor bis Zeilenanfang / Zeilenende.
Ctrl+U to clear everything before cursorCtrl+Y — Fügt zuvor mit Ctrl+U/K/W ausgeschnittenen Text ein (yank).
Ctrl+U then Ctrl+Y to restoreCtrl+L — Leert den Bildschirm (wie der Befehl clear).
Ctrl+LCtrl+Z — Suspendiert den aktuellen Vordergrundprozess.
Ctrl+Z, then bg to resume in background!! / !<n> / !<string> — Führt letzten Befehl / Befehl Nr. n / letzten Befehl, der mit string beginnt, erneut aus.
sudo !!Häufige Patterns
set -euo pipefail — Strict-Mode: bricht bei Fehlern, undefinierten Variablen und Pipe-Fehlern ab.
#!/bin/bash
set -euo pipefailDIR="$(cd "$(dirname "$0")" && pwd)" — Ermittelt zuverlässig das Verzeichnis des aktuellen Skripts.
DIR="$(cd "$(dirname "$0")" && pwd)"[[ -f file ]] || { echo 'Missing'; exit 1; } — Guard-Klausel: bricht früh ab, wenn eine benötigte Datei fehlt.
[[ -f config.yml ]] || { echo 'Config not found'; exit 1; }while IFS=',' read -r col1 col2; do ... done < file — Parst CSV- oder begrenzte Daten zeilenweise.
while IFS=',' read -r name age; do echo "$name is $age"; done < people.csvcommand -v <cmd> &> /dev/null — Prüft, ob ein Befehl auf dem System verfügbar ist.
command -v docker &> /dev/null || echo 'Docker not installed'mktemp — Erzeugt sicher eine temporäre Datei und gibt ihren Pfad aus.
TMP=$(mktemp); echo 'data' > "$TMP"; rm "$TMP" Fazit
Bash begleitet dich von der schnellen Einzeiler-Pipeline bis zum ausgewachsenen Deployment-Skript. Für die interaktive Arbeit zählen vor allem Tastenkürzel, History-Expansion und Pipes; für Skripte machen Parameter-Expansion, Arrays und saubere Funktionen den Unterschied. Aktiviere in jedem ernsthaften Skript set -euo pipefail, damit Fehler nicht stillschweigend durchrutschen, und quote deine Variablen konsequent ("$VAR"), um Wortsplitting und Globbing-Überraschungen zu vermeiden. Denk auch daran, wo deine Konfiguration landet: ~/.bashrc greift bei interaktiven Nicht-Login-Shells, ~/.bash_profile bei Login-Shells – wer das verwechselt, wundert sich über fehlende Aliase oder PATH-Einträge.
Weiterführende Links
- ubuntuusers-Wiki: Bash – deutschsprachige Einführung in Shell und Bash-Skripting
- GNU Bash Reference Manual – offizielle, vollständige Referenz zu allen Bash-Features (englisch)
- ShellCheck – Online-Linter, der häufige Fehler in Shell-Skripten aufspürt (englisch)