Dash vs Bash: POSIX-Unterschiede, die sh-Skripte brechen
AI generated
$_
#!/
Bash · POSIX · Portabilitaet · Linux
Dash vs Bash
warum /bin/sh nicht das ist, was man erwartet

Ein Skript mit #!/bin/sh laeuft auf dem eigenen Rechner problemlos und scheitert auf dem Debian Server mit einer kryptischen Fehlermeldung. Der Grund ist der Unterschied zwischen Dash und Bash: zwei Shells mit gemeinsamer POSIX Basis, aber sehr unterschiedlichem Funktionsumfang.

18 Min. Lesezeit Dash · POSIX sh · checkbashisms · ShellCheck Debian · Ubuntu · Alpine · macOS

1. Warum /bin/sh nicht Bash ist

In vielen Skripten steht #!/bin/sh im Shebang, doch wer glaubt, damit automatisch Bash Funktionen nutzen zu koennen, irrt auf jedem System mit Debian Abstammung. Seit 2009 verweist /bin/sh auf Debian und Ubuntu nicht mehr auf Bash, sondern auf Dash, die Debian Almquist Shell. Der Grund ist simpel: Dash startet spuerbar schneller als Bash, was beim Booten und bei Init Skripten messbar Zeit spart. Wer ein Skript mit #!/bin/sh schreibt und dabei Bash spezifische Syntax verwendet, bekommt auf solchen Systemen kryptische Fehlermeldungen statt eines funktionierenden Programms.

Der Unterschied zwischen Dash und Bash betrifft laengst nicht nur Debian selbst. Alpine Linux verweist /bin/sh auf BusyBox ash, einen weiteren minimalen POSIX nahen Interpreter. Auf macOS und den meisten RPM basierten Distributionen bleibt /bin/sh dagegen oft ein Bash Symlink oder eine eigene Implementierung. Diese Uneinheitlichkeit bedeutet konkret: Ein Skript, das auf dem eigenen Entwicklungsrechner klaglos laeuft, kann in einem Docker Container oder auf einem frisch aufgesetzten Debian Server sofort scheitern. Das Verstaendnis von Dash und Bash als zwei unterschiedliche Interpreter mit ueberlappender, aber nicht identischer Syntax ist deshalb Grundlage jeder portablen Automatisierung.

2. Was Dash ueberhaupt ist

Dash entstammt der Almquist Shell, einer schlanken Neuimplementierung der urspruenglichen Bourne Shell aus den spaeten 1980er Jahren. Debian uebernahm sie 1997 zunaechst als Alternative und machte sie 2006 zum offiziellen /bin/sh, um Bootzeiten zu verkuerzen. Das Designziel von Dash ist radikale Einfachheit: geringer Speicherbedarf, minimaler Funktionsumfang, strikte Ausrichtung an POSIX statt an Komfortfunktionen. Bash dagegen ist als interaktive, komfortable Shell fuer Entwickler und Administratoren gewachsen und hat ueber Jahrzehnte GNU spezifische Erweiterungen angesammelt.

Dieser Unterschied in der Zielsetzung erklaert, warum Dash und Bash trotz gemeinsamer POSIX Basis in der Praxis so unterschiedlich wirken. Wo Bash Arrays, Prozess Substitution und erweiterte Testoperatoren bietet, bleibt Dash bei dem, was der POSIX Standard verlangt und nicht mehr. Fuer Systemskripte, die beim Booten laufen, reicht das voellig aus. Fuer komplexere Automatisierung mit Datenstrukturen und Fehlerbehandlung stoesst Dash schnell an Grenzen, die Entwickler kennen sollten, bevor sie ein Skript mit #!/bin/sh beginnen.

3. Fehlende Features: Arrays, [[ ]] und mehr

Der auffaelligste Unterschied zwischen Dash und Bash sind Arrays. Bash bietet indizierte und assoziative Arrays als natives Sprachfeature, Dash kennt keines von beiden. Ein Skript mit declare -A config schlaegt unter Dash sofort mit einem Syntaxfehler fehl, nicht erst zur Laufzeit. Ebenso fehlt die erweiterte Testklammer [[ ]], die in Bash Mustervergleiche, Regex mit =~ und logische Verknuepfungen ohne Escaping ermoeglicht. Unter Dash steht nur die POSIX Testklammer [ ] zur Verfuegung, die deutlich restriktiver ist und bei falscher Quotierung zu stillen Fehlern fuehrt.

Auch Prozess Substitution wie <(befehl) und Hier Strings wie <<< "text" fehlen in Dash vollstaendig. Wer solche Konstrukte in einem vermeintlichen POSIX Skript verwendet, produziert unter Dash die Fehlermeldung Syntax error: Bad substitution. Ebenso fehlen Namensreferenzen fuer Rueckgabewerte, erweiterte Parameter Expansion fuer Klein und Grossschreibung und Mehrfachumleitung in einem einzigen Schritt. Wer von Bash zu Dash wechselt, muss diese Komfortfunktionen bewusst durch POSIX konforme Alternativen ersetzen.


#!/bin/sh
# This script LOOKS portable but uses Bash-only syntax
# Running it with dash script.sh instead of bash script.sh fails immediately

declare -A config    # syntax error under dash: arrays do not exist
config[env]="prod"

if [[ "$1" == "deploy" ]]; then   # syntax error under dash: [[ ]] not supported
  echo "Deploying to ${config[env]}"
fi

# POSIX-safe rewrite: no arrays, no [[ ]], works under dash AND bash
env_name="prod"
if [ "$1" = "deploy" ]; then
  printf '%s\n' "Deploying to $env_name"
fi

4. echo, printf und Escape Sequenzen

Ein besonders tueckischer Unterschied zwischen Dash und Bash liegt im Verhalten von echo. In Bash interpretiert echo Escape Sequenzen wie \n oder \t nur mit dem Flag -e, standardmaessig werden sie als literale Zeichen ausgegeben. In Dash dagegen interpretiert das eingebaute echo Escape Sequenzen automatisch, ganz ohne -e Flag, weil Dash dem aelteren System V Verhalten folgt. Ein Skript, das unter Bash echo "Zeile1\nZeile2" testet und literale Backslashes erwartet, produziert unter Dash zwei getrennte Zeilen und umgekehrt.

Diese Inkonsistenz ist so bekannt, dass POSIX empfiehlt, fuer portable Skripte statt echo grundsaetzlich printf zu verwenden, dessen Verhalten ueber alle Shells hinweg identisch definiert ist. printf format string ersetzt echo zuverlaessig, ohne sich um Shell spezifisches Escape Verhalten sorgen zu muessen. Wer in bestehenden Skripten viele echo Aufrufe mit Sonderzeichen hat, sollte diese vor einem Wechsel zwischen Dash und Bash systematisch durch printf ersetzen.


# Bash: -e is required to interpret escape sequences
echo "Line1\nLine2"        # bash default: prints literal backslash-n
echo -e "Line1\nLine2"     # bash with -e: prints two lines

# dash: escape sequences are interpreted WITHOUT -e
echo "Line1\nLine2"        # dash default: already prints two lines

# printf behaves identically in dash AND bash: the safe, portable choice
printf '%s\n' "Line1" "Line2"
printf 'Deploying %s to %s\n' "app" "$env_name"

5. Funktionsdefinition und lokale Variablen

Auch bei Funktionsdefinitionen zeigt sich der Unterschied zwischen Dash und Bash. Bash akzeptiert sowohl die POSIX Syntax name() { ... } als auch das Schluesselwort function name { ... }. Dash unterstuetzt ausschliesslich die erste, POSIX konforme Form, das function Schluesselwort erzeugt einen Syntaxfehler. Wer Bibliotheksfunktionen fuer beide Shells schreiben will, sollte deshalb konsequent bei der POSIX Form bleiben, auch wenn die Bash Form vertrauter wirkt.

Bei lokalen Variablen wird es komplizierter: local ist kein Teil des POSIX Standards, wird aber von Dash seit vielen Jahren als De facto Erweiterung unterstuetzt. Der Unterschied zu Bash liegt in Details: local kann in Dash nicht mit Zuweisung und Befehlssubstitution in einer Zeile kombiniert werden, ohne den Exit Code des Befehls zu verlieren, ein Verhalten, das in Bash mit set -e ebenfalls Probleme macht, in Dash aber oefter uebersehen wird. Die sichere Variante ist immer, Deklaration und Zuweisung in zwei getrennten Zeilen zu schreiben.

6. Arithmetik und Testoperatoren

Arithmetische Ausdruecke funktionieren in Dash und Bash ueber den POSIX Standardmechanismus mit doppelten Klammern weitgehend identisch, doch die komfortable Bash Erweiterung als eigenstaendiges Kommando fuer Bedingungen fehlt in Dash. Ein Ausdruck wie if (( count > 0 )); then funktioniert unter Bash, unter Dash muss man auf if [ "$count" -gt 0 ]; then ausweichen. Wer Zaehler und Bedingungen in Skripten mit doppelten Klammern schreibt, muss diese Stellen beim Wechsel zu Dash manuell anpassen.

Bei Testoperatoren fehlt in Dash zusaetzlich der Mustervergleich innerhalb der eckigen Klammer und die Regex Pruefung mit =~. Die POSIX Form kennt nur den Gleichheitsoperator fuer Strings und die numerischen Vergleichsoperatoren -eq, -ne, -lt, -gt. Ein Skript mit einem doppelten Gleichheitszeichen laeuft unter Dash zwar oft noch, weil manche Implementierungen es als Alias akzeptieren, ist aber nicht garantiert portabel und sollte in POSIX Skripten durch den einfachen Gleichheitsoperator ersetzt werden.


count=5
env_name="prod"

# Bash-only: arithmetic command and pattern matching
if (( count > 0 )); then echo "positive"; fi
if [[ "$env_name" == prod* ]]; then echo "production match"; fi

# POSIX-safe: works under dash, bash and busybox ash
if [ "$count" -gt 0 ]; then echo "positive"; fi
case "$env_name" in
  prod*) echo "production match" ;;
esac

7. Bashismen mit checkbashisms aufspueren

Statt jede Zeile manuell auf Bash spezifische Syntax zu pruefen, gibt es dedizierte Werkzeuge. Das Debian Paket checkbashisms durchsucht ein Skript gezielt nach Konstrukten, die unter Dash nicht funktionieren, und listet jede Fundstelle mit Zeilennummer auf. Es ist das Standardwerkzeug, mit dem Debian Maintainer ihre Paketskripte gegen den Dash Wechsel von 2009 abgesichert haben, und eignet sich hervorragend, um bestehende Skripte vor einer Migration zu pruefen.

Ergaenzend liefert ShellCheck mit der Option --shell=sh dieselbe Klasse von Warnungen direkt in der Entwicklungsumgebung, inklusive Erklaerungen und Verweisen auf die jeweilige POSIX Regel. Wer testen will, wie sich ein Skript unter Dash tatsaechlich verhaelt, kann es zusaetzlich temporaer mit dash skript.sh statt bash skript.sh ausfuehren, denn auf den meisten Debian Systemen ist Dash bereits vorinstalliert. Die Kombination aus statischer Analyse und echter Ausfuehrung unter Dash deckt praktisch alle relevanten Unterschiede zwischen Dash und Bash zuverlaessig auf.


#!/usr/bin/env bash
set -euo pipefail

# Install and run checkbashisms against every script claiming #!/bin/sh
sudo apt-get install -y devscripts
checkbashisms deploy.sh
# possible warning: 'declare' is bash specific, line 4

# ShellCheck with explicit POSIX target
shellcheck --shell=sh deploy.sh

# Real execution test: does it actually run under dash?
dash deploy.sh || echo "[FAIL] script is not dash-compatible"

8. Migrationsstrategie fuer Teams

Die zentrale Entscheidung lautet: Soll ein Skript wirklich POSIX portabel unter Dash laufen, oder reicht es, Bash explizit vorauszusetzen? Fuer einfache Systemskripte, Init Hooks und Paket Postinst Skripte ist die Antwort meist Dash, weil diese Skripte auf moeglichst vielen Systemen ohne Zusatzabhaengigkeit laufen sollen. Fuer komplexere Automatisierung mit Arrays, Fehlerbehandlung und Parallelisierung lohnt sich der Umweg ueber Dash Kompatibilitaet selten, hier ist ein expliziter #!/usr/bin/env bash Shebang die pragmatischere Wahl.

Wichtig ist vor allem Konsistenz: Ein Shebang mit #!/bin/sh, das faktisch Bash Syntax enthaelt, ist die gefaehrlichste Kombination, weil es auf den meisten Entwicklungsrechnern klaglos laeuft und erst auf einem Dash System bricht, oft erst in der Produktion. Teams sollten deshalb festlegen, welche Skript Kategorie welchen Shebang bekommt, und diese Regel mit checkbashisms in der CI Pipeline automatisch durchsetzen, statt sich auf manuelle Code Reviews zu verlassen.

9. Dash vs Bash im direkten Vergleich

Die folgende Tabelle fasst die wichtigsten Unterschiede zwischen Dash und Bash zusammen, die in der Praxis am haeufigsten zu Fehlern fuehren.

Feature Bash Dash Konsequenz
Arrays indiziert und assoziativ nicht vorhanden Syntaxfehler beim Parsen, nicht erst zur Laufzeit
Testklammer [[ ]] mit Regex nur [ ] POSIX case statt Mustervergleich noetig
echo Escapes nur mit -e automatisch aktiv printf statt echo fuer Portabilitaet
Prozess Substitution <(befehl) nicht vorhanden temporaere Datei als Ersatz noetig
Startgeschwindigkeit langsamer spuerbar schneller Debian Bootzeit Argument fuer Dash als /bin/sh

In der Summe zeigt der Vergleich, dass Dash bewusst auf Komfortfunktionen verzichtet, die Bash im Laufe der Zeit angesammelt hat. Fuer ein Skript, das garantiert auf jedem POSIX System laeuft, egal ob Dash, BusyBox ash oder eine andere schlanke Shell, ist der Verzicht auf diese Funktionen der Preis fuer echte Portabilitaet. Die Entscheidung zwischen Dash und Bash sollte deshalb bewusst und dokumentiert getroffen werden, nicht durch Zufall im Shebang.

Mironsoft

Shell-Automatisierung, DevOps-Tooling und portable Infrastruktur-Skripte

Skripte, die auf jedem System zuverlaessig laufen?

Wir pruefen bestehende Shell Skripte auf Bashismen, machen sie mit checkbashisms und ShellCheck portabel und legen fest, wo Dash und wo Bash die richtige Wahl ist.

Portabilitaets-Audit

checkbashisms und ShellCheck ueber den gesamten Skript-Bestand laufen lassen

Migration

Kritische Systemskripte POSIX konform umbauen, ohne Funktionalitaet zu verlieren

CI-Integration

Shebang-Regeln automatisch pruefen, bevor ein Skript gemergt wird

10. Zusammenfassung

Der Unterschied zwischen Dash und Bash entscheidet in der Praxis darueber, ob ein Skript mit #!/bin/sh auf Debian, Ubuntu und Alpine tatsaechlich laeuft oder mit einem Syntaxfehler abbricht. Dash verzichtet bewusst auf Arrays, die erweiterte Testklammer, Prozess Substitution und viele weitere Bash Erweiterungen, um schnell und schlank zu bleiben. Wer diese Unterschiede kennt, kann gezielt entscheiden, wann ein Skript wirklich POSIX portabel geschrieben werden muss und wann ein expliziter Bash Shebang die einfachere und ehrlichere Wahl ist.

Werkzeuge wie checkbashisms und ShellCheck mit der Option --shell=sh nehmen einem Team die manuelle Pruefung ab und finden Bashismen zuverlaessig, bevor sie in Produktion Probleme verursachen. Die Kombination aus klaren Team Regeln, automatisierter Pruefung in der CI Pipeline und einem bewussten Umgang mit Dash und Bash als zwei unterschiedlichen Werkzeugen ist der zuverlaessigste Weg zu wirklich portablen Shell Skripten.

Dash vs Bash: das Wichtigste auf einen Blick

Warum es passiert

/bin/sh zeigt auf Debian und Ubuntu seit 2009 auf Dash, nicht auf Bash. Ein Shebang mit #!/bin/sh und Bash Syntax bricht dort sofort.

Fehlende Features

Arrays, [[ ]], Prozess Substitution, Hier Strings und function Schluesselwort existieren in Dash nicht.

Werkzeuge

checkbashisms und shellcheck --shell=sh finden Bashismen automatisch, dash skript.sh testet die reale Ausfuehrung.

Entscheidung

Einfache Systemskripte: Dash kompatibel halten. Komplexe Automatisierung: expliziter #!/usr/bin/env bash Shebang.

11. FAQ: Dash vs Bash

1Was ist der Unterschied zwischen Dash und Bash?
Dash ist schlank und POSIX nah, Bash bietet Arrays, [[ ]] und viele Komfortfunktionen. Beide teilen eine gemeinsame POSIX Basis.
2Warum ist /bin/sh nicht Bash?
Debian und Ubuntu verlinken /bin/sh seit 2009 auf Dash, weil es schneller startet. Bash Syntax bricht dort mit einem Shebang von #!/bin/sh.
3Kann ich Arrays in Dash verwenden?
Nein, Dash kennt keine Arrays. declare -A oder array=(...) erzeugen einen Syntaxfehler beim Parsen.
4Warum verhaelt sich echo unterschiedlich?
Bash braucht -e fuer Escape Sequenzen, Dash interpretiert sie automatisch. printf ist die portable Alternative zu echo.
5Funktioniert function name { } in Dash?
Nein, nur die POSIX Form name() { ... } funktioniert unter Dash. Das function Schluesselwort ist Bash spezifisch.
6[ ] vs [[ ]] in Dash?
Nur [ ] steht in Dash zur Verfuegung. Mustervergleiche muessen mit case statt mit [[ ]] realisiert werden.
7Wie finde ich Bashismen?
checkbashisms oder shellcheck --shell=sh listen jede Fundstelle mit Zeilennummer auf. Zusaetzlich mit dash skript.sh real testen.
8sh oder bash im Shebang?
Einfache, wirklich POSIX konforme Skripte: sh. Komplexe Automatisierung mit Arrays: expliziter bash Shebang statt fragiler Kompatibilitaet.
9Funktioniert local in Dash?
Ja, als De facto Erweiterung. Deklaration und Zuweisung getrennt schreiben, um den Exit Code nicht zu verlieren.
10Ist Dash schneller als Bash?
Ja, durch geringeren Funktionsumfang startet Dash spuerbar schneller, was bei vielen Init Skripten beim Booten messbar Zeit spart.