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_FILE

export 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=0

Parameter-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^^}"  # HELLO

String-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}"  # World

Arrays

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]}"  # green

echo "${ARR[@]}" — Expandiert alle Array-Elemente.

echo "${COLORS[@]}"

echo "${#ARR[@]}" — Ermittelt die Anzahl der Elemente eines Arrays.

echo "${#COLORS[@]}"  # 3

ARR+=(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]=443

for item in "${ARR[@]}"; do ... done — Iteriert über alle Array-Elemente.

for c in "${COLORS[@]}"; do echo "$c"; done

Bedingungen

if [[ condition ]]; then ... fi — Einfaches if-Statement mit erweiterten Test-Klammern.

if [[ -f 'config.txt' ]]; then echo 'Found'; fi

if [[ 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"; done

for (( i=0; i<n; i++ )); do ... done — C-artige for-Schleife mit Zähler.

for (( i=1; i<=5; i++ )); do echo "$i"; done

for VAR in {start..end}; do ... done — Iteriert über einen Zahlenbereich per Brace-Expansion.

for i in {1..10}; do echo "$i"; done

while [[ condition ]]; do ... done — Schleife, solange die Bedingung wahr ist.

while [[ $count -lt 10 ]]; do ((count++)); done

while 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"; done

until [[ condition ]]; do ... done — Schleife, bis die Bedingung wahr wird.

until [[ -f /tmp/ready ]]; do sleep 1; done

Funktionen

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 5

return <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.txt

cmd >> file — Leitet stdout in eine Datei um (hängt an).

echo 'line' >> log.txt

cmd 2> file — Leitet stderr in eine Datei um.

find / -name '*.conf' 2> /dev/null

cmd &> file — Leitet stdout und stderr in eine Datei um.

make &> build.log

cmd 2>&1 — Leitet stderr nach stdout um (kombiniert die Streams).

command 2>&1 | tee output.log

cmd1 | cmd2 — Leitet stdout von cmd1 an stdin von cmd2 weiter (Pipe).

ps aux | grep nginx

cmd1 |& cmd2 — Leitet stdout und stderr an cmd2 weiter.

make |& less

cmd <<< '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 -l

fg %<n> — Holt Hintergrund-Job Nummer n in den Vordergrund.

fg %1

bg %<n> — Setzt einen gestoppten Job im Hintergrund fort.

bg %1

wait [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 TERM

Befehlsverkettung

cmd1 && cmd2 — Führt cmd2 nur aus, wenn cmd1 erfolgreich war (Exit-Code 0).

mkdir build && cd build

cmd1 || 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 command

Ctrl+A / Ctrl+E — Bewegt den Cursor an den Anfang / das Ende der Zeile.

Ctrl+A to jump to start

Ctrl+W / Alt+D — Löscht das Wort vor / nach dem Cursor.

Ctrl+W to delete previous word

Ctrl+U / Ctrl+K — Löscht von Cursor bis Zeilenanfang / Zeilenende.

Ctrl+U to clear everything before cursor

Ctrl+Y — Fügt zuvor mit Ctrl+U/K/W ausgeschnittenen Text ein (yank).

Ctrl+U then Ctrl+Y to restore

Ctrl+L — Leert den Bildschirm (wie der Befehl clear).

Ctrl+L

Ctrl+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 pipefail

DIR="$(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.csv

command -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.

Verwandte Kommandos

  • zsh – Z-Shell, erweiterte interaktive Shell mit umfangreicher Vervollständigung
  • fish – benutzerfreundliche Shell mit Syntax-Highlighting ab Werk
  • jobs – Hintergrund- und gestoppte Jobs der aktuellen Shell verwalten