# Bash — Shell-Skripting und interaktive Kommandozeile

> Praxis-Guide zu Bash: Variablen, Parameter-Expansion, Schleifen, Funktionen, Umleitungen und robuste Skripte mit set -euo pipefail.

Source: https://www.jpkc.com/db/cheatsheets/shell-system/bash/

<!-- PROSE:intro -->
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.
<!-- PROSE:intro:end -->

## Variablen & Strings

`VAR='value'` — Weist einer Variablen einen Wert zu. Keine Leerzeichen ums Gleichheitszeichen.

```bash
NAME='World'
```

`echo "$VAR"` — Expandiert die Variable in doppelten Anführungszeichen. Einfache Anführungszeichen verhindern die Expansion.

```bash
echo "Hello $NAME"
```

`echo "${VAR}text"` — Klammern grenzen den Variablennamen vom umgebenden Text ab.

```bash
echo "${NAME}_backup"
```

`readonly VAR='value'` — Deklariert eine schreibgeschützte (konstante) Variable.

```bash
readonly PI='3.14159'
```

`unset VAR` — Entfernt eine Variable.

```bash
unset TEMP_FILE
```

`export VAR='value'` — Exportiert die Variable an Kindprozesse (Umgebungsvariable).

```bash
export PATH="$HOME/bin:$PATH"
```

`local VAR='value'` — Deklariert eine lokale Variable innerhalb einer Funktion.

```bash
local count=0
```

## Parameter-Expansion

`${VAR:-default}` — Nutzt den Default-Wert, wenn die Variable nicht gesetzt oder leer ist.

```bash
echo "${USER:-anonymous}"
```

`${VAR:=default}` — Weist den Default-Wert zu, wenn die Variable nicht gesetzt oder leer ist.

```bash
echo "${LANG:=en_US.UTF-8}"
```

`${VAR:+alternate}` — Nutzt den Alternativwert, wenn die Variable gesetzt und nicht leer ist.

```bash
echo "${DEBUG:+--verbose}"
```

`${VAR:?error message}` — Gibt eine Fehlermeldung aus und bricht ab, wenn die Variable nicht gesetzt oder leer ist.

```bash
${API_KEY:?API_KEY must be set}
```

`${#VAR}` — Ermittelt die Länge einer String-Variablen.

```bash
echo "Length: ${#NAME}"
```

`${VAR^^} / ${VAR,,}` — Wandelt in Groß- / Kleinbuchstaben um (Bash 4+).

```bash
echo "${name^^}"  # HELLO
```

## String-Manipulation

`${VAR#pattern}` — Entfernt die kürzeste Übereinstimmung des Musters am Anfang.

```bash
FILE='archive.tar.gz'; echo "${FILE#*.}"  # tar.gz
```

`${VAR##pattern}` — Entfernt die längste Übereinstimmung des Musters am Anfang.

```bash
PATH='/home/user/file.txt'; echo "${PATH##*/}"  # file.txt
```

`${VAR%pattern}` — Entfernt die kürzeste Übereinstimmung des Musters am Ende.

```bash
FILE='photo.jpg'; echo "${FILE%.*}"  # photo
```

`${VAR%%pattern}` — Entfernt die längste Übereinstimmung des Musters am Ende.

```bash
FILE='backup.tar.gz'; echo "${FILE%%.*}"  # backup
```

`${VAR/pattern/replacement}` — Ersetzt das erste Vorkommen des Musters.

```bash
echo "${STR/foo/bar}"
```

`${VAR//pattern/replacement}` — Ersetzt alle Vorkommen des Musters.

```bash
echo "${PATH//:/ }"
```

`${VAR:offset:length}` — Extrahiert eine Teilzeichenkette ab Offset mit angegebener Länge.

```bash
STR='Hello World'; echo "${STR:6:5}"  # World
```

## Arrays

`ARR=(val1 val2 val3)` — Deklariert ein indiziertes Array.

```bash
COLORS=(red green blue)
```

`echo "${ARR[0]}"` — Greift per Index auf ein Array-Element zu (0-basiert).

```bash
echo "${COLORS[1]}"  # green
```

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

```bash
echo "${COLORS[@]}"
```

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

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

`ARR+=(val4)` — Hängt ein Element an ein Array an.

```bash
COLORS+=(yellow)
```

`declare -A MAP` — Deklariert ein assoziatives Array (Bash 4+).

```bash
declare -A PORTS; PORTS[http]=80; PORTS[https]=443
```

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

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

## Bedingungen

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

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

`if [[ cond ]]; then ... else ... fi` — If-else-Statement.

```bash
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.

```bash
[[ -f /etc/hosts ]] && echo 'Exists'
```

`[[ -d <dir> ]]` — Prüft, ob ein Verzeichnis existiert.

```bash
[[ -d /tmp ]] && echo 'Directory exists'
```

`[[ -z "$VAR" ]]` — Prüft, ob ein String leer ist (Länge null).

```bash
[[ -z "$INPUT" ]] && echo 'Empty input'
```

`[[ -n "$VAR" ]]` — Prüft, ob ein String nicht leer ist.

```bash
[[ -n "$USER" ]] && echo "User: $USER"
```

`[[ "$A" == "$B" ]]` — Vergleicht zwei Strings auf Gleichheit.

```bash
[[ "$answer" == 'yes' ]] && proceed
```

`[[ "$A" =~ regex ]]` — Regex-Test (Extended Regular Expressions).

```bash
[[ "$email" =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]] && echo 'Valid'
```

## Schleifen

`for VAR in list; do ... done` — Iteriert über eine Liste von Elementen.

```bash
for f in *.txt; do echo "$f"; done
```

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

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

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

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

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

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

`while read -r line; do ... done < <file>` — Liest eine Datei zeilenweise.

```bash
while read -r line; do echo "$line"; done < data.txt
```

`<command> | while read -r line; do ... done` — Verarbeitet die Ausgabe eines Befehls zeilenweise.

```bash
find . -name '*.log' | while read -r f; do wc -l "$f"; done
```

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

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

## Funktionen

`fname() { ... }` — Definiert eine Funktion. Bevorzugte Syntax gegenüber dem function-Schlüsselwort.

```bash
greet() { echo "Hello, $1"; }
```

`$1, $2, ..., $@, $#` — Greift auf Funktionsargumente zu. $@ sind alle Argumente, $# ist deren Anzahl.

```bash
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.

```bash
is_root() { [[ $EUID -eq 0 ]] && return 0 || return 1; }
```

`result=$(fname arg)` — Fängt die Funktionsausgabe per Command-Substitution in einer Variablen ein.

```bash
upper() { echo "${1^^}"; }; NAME=$(upper 'hello')
```

## Arithmetik

`$(( expression ))` — Arithmetische Expansion. Unterstützt +, -, *, /, %, **.

```bash
echo "$(( 5 + 3 ))"  # 8
```

`(( VAR++ )) / (( VAR-- ))` — Inkrementiert / dekrementiert eine Variable.

```bash
count=0; (( count++ )); echo "$count"  # 1
```

`(( VAR += n ))` — Zusammengesetzte Zuweisungsoperatoren (+=, -=, *=, /=, %=).

```bash
total=100; (( total -= 25 )); echo "$total"  # 75
```

`$(( RANDOM % n ))` — Erzeugt eine Zufallszahl zwischen 0 und n-1.

```bash
echo "Dice: $(( RANDOM % 6 + 1 ))"
```

## Umleitung & Pipes

`cmd > file` — Leitet stdout in eine Datei um (überschreibt).

```bash
echo 'hello' > output.txt
```

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

```bash
echo 'line' >> log.txt
```

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

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

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

```bash
make &> build.log
```

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

```bash
command 2>&1 | tee output.log
```

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

```bash
ps aux | grep nginx
```

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

```bash
make |& less
```

`cmd <<< 'string'` — Here-String. Übergibt einen String als stdin an einen Befehl.

```bash
grep 'error' <<< "$log_output"
```

## Prozesssteuerung

`cmd &` — Führt einen Befehl im Hintergrund aus.

```bash
sleep 60 &
```

`jobs` — Listet Hintergrund-Jobs der aktuellen Shell auf.

```bash
jobs -l
```

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

```bash
fg %1
```

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

```bash
bg %1
```

`wait [pid]` — Wartet auf das Ende eines Hintergrundprozesses. Ohne Argument auf alle.

```bash
cmd1 & cmd2 &; wait
```

`$!` — PID des zuletzt gestarteten Hintergrundprozesses.

```bash
server & echo "PID: $!"
```

`$$` — PID der aktuellen Shell.

```bash
echo "Script PID: $$"
```

`trap 'cmd' SIGNAL` — Führt einen Befehl aus, wenn ein Signal empfangen wird.

```bash
trap 'rm -f /tmp/lockfile; exit' EXIT INT TERM
```

## Befehlsverkettung

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

```bash
mkdir build && cd build
```

`cmd1 || cmd2` — Führt cmd2 nur aus, wenn cmd1 fehlschlägt (Exit-Code ungleich 0).

```bash
grep -q 'error' log.txt || echo 'No errors'
```

`cmd1; cmd2` — Führt cmd2 nach cmd1 aus, unabhängig vom Exit-Status.

```bash
echo 'Start'; date; echo 'End'
```

`{ cmd1; cmd2; }` — Gruppiert Befehle in der aktuellen Shell (Semikolon und Leerzeichen beachten).

```bash
{ echo 'Header'; cat data.txt; } > report.txt
```

`(cmd1; cmd2)` — Führt Befehle in einer Subshell aus (ohne Einfluss auf die Eltern-Shell).

```bash
(cd /tmp && tar xzf archive.tar.gz)
```

## Spezielle Variablen

`$?` — Exit-Status des letzten Befehls (0 = Erfolg).

```bash
grep -q 'root' /etc/passwd; echo "$?"
```

`$0` — Name des aktuellen Skripts oder der Shell.

```bash
echo "Script: $0"
```

`$# / $@ / $*` — Anzahl der Argumente / alle Argumente (als einzelne Wörter / als ein Wort).

```bash
echo "$# arguments: $@"
```

`$_` — Letztes Argument des vorigen Befehls.

```bash
mkdir mydir; cd "$_"
```

`$RANDOM` — Zufällige Ganzzahl zwischen 0 und 32767.

```bash
echo "$RANDOM"
```

`$SECONDS` — Sekunden seit dem Start der Shell. Lässt sich zurücksetzen.

```bash
SECONDS=0; sleep 2; echo "Elapsed: ${SECONDS}s"
```

`$LINENO` — Aktuelle Zeilennummer im Skript.

```bash
echo "Error at line $LINENO"
```

## Tastenkürzel

`Ctrl+R` — Rückwärtssuche in der Befehls-History.

```bash
Ctrl+R, then type part of a previous command
```

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

```bash
Ctrl+A to jump to start
```

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

```bash
Ctrl+W to delete previous word
```

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

```bash
Ctrl+U to clear everything before cursor
```

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

```bash
Ctrl+U then Ctrl+Y to restore
```

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

```bash
Ctrl+L
```

`Ctrl+Z` — Suspendiert den aktuellen Vordergrundprozess.

```bash
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.

```bash
sudo !!
```

## Häufige Patterns

`set -euo pipefail` — Strict-Mode: bricht bei Fehlern, undefinierten Variablen und Pipe-Fehlern ab.

```bash
#!/bin/bash
set -euo pipefail
```

`DIR="$(cd "$(dirname "$0")" && pwd)"` — Ermittelt zuverlässig das Verzeichnis des aktuellen Skripts.

```bash
DIR="$(cd "$(dirname "$0")" && pwd)"
```

`[[ -f file ]] || { echo 'Missing'; exit 1; }` — Guard-Klausel: bricht früh ab, wenn eine benötigte Datei fehlt.

```bash
[[ -f config.yml ]] || { echo 'Config not found'; exit 1; }
```

`while IFS=',' read -r col1 col2; do ... done < file` — Parst CSV- oder begrenzte Daten zeilenweise.

```bash
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.

```bash
command -v docker &> /dev/null || echo 'Docker not installed'
```

`mktemp` — Erzeugt sicher eine temporäre Datei und gibt ihren Pfad aus.

```bash
TMP=$(mktemp); echo 'data' > "$TMP"; rm "$TMP"
```

<!-- PROSE:outro -->
## 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](https://wiki.ubuntuusers.de/Bash/) – deutschsprachige Einführung in Shell und Bash-Skripting
- [GNU Bash Reference Manual](https://www.gnu.org/software/bash/manual/) – offizielle, vollständige Referenz zu allen Bash-Features (englisch)
- [ShellCheck](https://www.shellcheck.net/) – Online-Linter, der häufige Fehler in Shell-Skripten aufspürt (englisch)
<!-- PROSE:outro:end -->

## Verwandte Kommandos

- [zsh](https://www.jpkc.com/db/cheatsheets/shell-system/zsh/) – Z-Shell, erweiterte interaktive Shell mit umfangreicher Vervollständigung
- [fish](https://www.jpkc.com/db/cheatsheets/shell-system/fish/) – benutzerfreundliche Shell mit Syntax-Highlighting ab Werk
- [jobs](https://www.jpkc.com/db/cheatsheets/shell-system/jobs/) – Hintergrund- und gestoppte Jobs der aktuellen Shell verwalten

