Mit SECUSTAFF die besten Bash (Shell) Experten finden
Wenn Sie auf der Suche nach einem Bash (Shell) Spezialisten sind, dann sind Sie bei uns richtig. Ganz gleich ob Sie Mitarbeiter für eine unbefristete Festanstellung oder Freelancer für Ihr IT Projekt suchen.

Sie sind auf der Suche nach einem Bash (Shell) Spezialisten? Senden Sie uns Ihre Position einfach und unverbindlich per E-Mail.
E-Mail sendenDas Wichtigste zum Thema Bash (Shell)
Wir wissen nicht alles, aber wir wissen, wo wir es finden. Wenn Sie auch noch etwas zum Thema Bash (Shell) wissen möchten, finden Sie hier sicherlich die Antwort. Und falls Sie einen Experten benötigen, der all dieses Wissen bereits aufgebaut und erfolgreich in der Praxis eingesetzt hat, dann sprechen Sie uns an, wir finden für Sie die besten verfügbaren Bash (Shell) Fachkräfte, egal ob Sie einen Freelancer für einen Tag benötigen oder einen Mitarbeiter für eine dauerhafte Festanstellung. Wir stehen für Secure Staffing.
Bash (Shell)
Bash (auch BASH oder bash), die Bourne-again shell, ist eine freie Unix-Shell unter GPL.
Als Shell ist Bash eine Mensch-Maschine-Schnittstelle, die eine Umgebung (englisch environment) bereitstellt, in der zeilenweise Texteingaben und -ausgaben möglich sind. Letzteres erfolgt über die Befehlszeile, in die Befehle eingetippt und durch Betätigen der Eingabetaste eingegeben werden. Gleichzeitig ist Bash eine Skriptsprache, die schon beim Booten vieler Linux-Distributionen zum Einsatz kommt.
Bash ist elementarer Bestandteil des unixähnlichen Betriebssystems GNU und gehört zum GNU-Projekt. Auch bei den meisten auf GNU/Linux aufbauenden Betriebssystemen ist Bash die voreingestellte Shell. Darüber hinaus war Bash 3.x von 2003 bis 2019 die voreingestellte Shell in macOS von Apple (10.3–10.14) – wurde allerdings aus lizenzrechtlichen Gründen nie auf Version 4.0 oder höher aktualisiert.
Der Name Bash ist im Englischen mehrdeutig ([to] bash, [the] bash) und erfuhr im Laufe der Zeit weitere, meist humoristische Bedeutungen.
Geschichte
Brian Fox schrieb die Bash 1987 für das GNU-Projekt und wurde dabei von der FSF bezahlt.[4] 1990 wurde das Projekt von Chet Ramey übernommen. Version 3 erschien am 27. Juli 2004. Version 4 erschien am 20. Februar 2009 und brachte einige Neuerungen mit sich. Darunter sind eine neue Ausgabeumleitung, assoziative Arrays und eine neue Wildcard (**).[5][6] Am 7. Januar 2019 folgte Bash 5, zu den Neuerungen zählten diesmal etliche ergänzte Shellvariablen, eine flexiblere Handhabung der Historie und die Möglichkeit, die Werte lokaler Variablen aus einem übergeordneten Scope zu übernehmen.[7]
Funktionalität
Aufgaben und Einsatzmöglichkeiten
- Grundfunktionen
- beliebige Kommandozeilen ausführen (beliebige Zeichen im Pfad des Kommandos und in den Argumenten; eindeutige Trennung der Argumente)
- Handhabung der Ein- und Ausgaben von Kommandos (Umleitung, Pipelines)
- Reaktion auf Erfolgs- und Fehler-Exit-Codes
- Ausführung von Scripten / Shellprogrammierung
- Expansionen und Substitutionen
- Kontrollstrukturen
- Schleifen
- Tests der Umgebung (Dateisystem, Eingabe- und Ausgabedeskriptoren, Umgebungsvariablen, UID)
- Bereitstellung von Informationen über den Zustand der Shell
- Erzeugung beliebiger Zeichen
- Reaktion auf Signale
- Debugging
- Arbeitserleichterungen v. a. in interaktiven Shells
- der Prompt
- Aliase, Shellfunktionen
- Editieren der Kommandozeile (Readline)
- Verwaltung von Vordergrund- und Hintergrundprozessen
- Kommando-Historie
- Vervollständigung von Kommandos und Argumenten (mit Standardmethoden und frei programmierbar)
- Verzeichnis-Stack (
pushd,popd,dirs) - Konfiguration des Verhaltens der Shell (v. a. (De-)Aktivierung von Funktionalitäten)
- Schutzmechanismen (undefinierte/leere Variablen; nichtgehandhabte Fehler von Kommandos; Überschreiben von Dateien; Ressourcenlimits; riskante Funktionalitäten)
- Reaktion auf nicht gefundene Kommandos
Vergleich mit anderen Shells
Die Shell ist weitgehend kompatibel zur Bourne-Shell (sh) und beherrscht zusätzlich sowohl die meisten Funktionen der Kornshell (ksh) als auch Teile der C-Shell-Syntax, wie zum Beispiel die Historie zuvor eingegebener Befehle, die $RANDOM-Variable und die POSIX-Form der Befehlssubstitution $(...) wurden übernommen. Einige Elemente wie etwa die **-Wildcard wurden von der Z shell (zsh) übernommen. Auch wurde sie um Funktionen wie z. B. die Ganzzahlarithmetik ohne die Ausführung externer Prozesse und Vereinfachung der I/O-Umleitungen erweitert. Bash bietet anhand ihrer benutzerspezifischen Konfigurationsdatei ~/.bashrc außerdem die Möglichkeit, jedem Benutzer eigene Einstellungen, wie eine individuelle Gestaltung der Eingabeaufforderung (englisch prompt), sitzungsübergreifend zu ermöglichen.
die Kommandozeile
Die Kommandozeile (im weiteren Sinn) aus Sicht des aufgerufenen Programms besteht aus
- dem physischen Pfad der ausgeführten Datei
- dem vom aufrufenden Prozess vorgegebenen Namen des Prozesses
- den Argumenten
- der Standardeingabe (
stdin), Standardausgabe (stdout) und Standardfehlerausgabe (stderr) - den Umgebungsvariablen
Aus Sicht der Shell ist eine Kommandozeile die Eingabe des Benutzers, die dramatisch komplizierter sein kann als das Ergebnis für das aufgerufene Programm. Die Bash erzeugt aus der Eingabe und weiteren Informationen die Daten, aus denen am Ende der Kernel das Programm startet. Der Begriff Kommandozeile ist nicht scharf abgegrenzt; er wird in mehreren, dicht beieinanderliegenden Zusammenhängen verwendet:
- die Kommandozeile, mit der die Bash selber aufgerufen wird, z. B.
bash -vxc 'echo ${HOME}' - die Eingabe des Benutzers bzw. die Zeilen eines Scripts; eine einzelne Zeile kann mehrere Kommandos enthalten, und ein einzelnes Kommando kann sich über mehrere Zeilen erstrecken
- der Code, der von der Bash gerade für die Ausführung vorbereitet wird, also ein einfaches oder zusammengesetztes Kommando
Die folgende Tabelle illustriert die Schritte, mit denen die Bash die Eingabezeile bearbeitet. Die betroffenen Mechanismen werden in den folgenden Abschnitten erläutert. Die Reihenfolge orientiert sich am qualitativen Ergebnis aus Benutzersicht, nicht an den Details des Bash-Parsers. Spalten:
- S: Szenarien
- Erläuterung: Zw.-Erg. == Zwischenergebnis
- B: ggf. mehrere Beispiele pro Szenario
- Zusatzinformationen: Änderungen an der Vorbereitung des neuen Prozesses (Programms), die weder Programmpfad noch Argument sind.
Formatierungen:
- Der
Pfad des Programms, der dem Kernel übergeben wird. - Argumente, die dem Kernel übergeben werden
- Teil der Eingabe, der im nächsten Schritt bearbeitet wird
- Teil der Eingabe, der für die aktuelle Kommandozeile nicht relevant ist
- einzelnes Wort nach Word-Splitting
- Teil der Kommandozeile ohne Word-Splitting (nach Quoting und Quote-Removal)
Annahmen:
- allgemein
- aktueller Benutzer:
user
- aktueller Benutzer:
- History-Expansion
- vorheriges Kommando:
ls -l /root
- vorheriges Kommando:
- Aliase
alias ls='ls --color=auto'alias aliastest='echo foo ; echo bar'
- Word-Splitting
- var='foo bar'
- ar=(foo bar)
- Pathname-Expansion
rm -r *.txt ; touch README.txtrm -r *.sh ; touch script.1.sh script.2.sh
- Quote-Removal
var='"'
Die unterschiedlichen Elemente einer Kommandozeile werden in der Reihenfolge abgearbeitet, in der sie in der Tabelle erscheinen (also Quoting zuerst).
| Arbeitsschritt | S | Erläuterung | B | Kommandozeile | Zusatzinformationen | |
|---|---|---|---|---|---|---|
| Die folgenden Arbeitsschritte finden nacheinander statt | ||||||
| Quoting | 1 | Eingabe mit Quoting | '/schlechter pfad/zum/script.sh' homebackup | |||
| Zw.-Erg. | /schlechter pfad/zum/script.sh homebackup | |||||
| 2 | Eingabe ohne Quoting | /schlechter pfad/zum/script.sh homebackup | ||||
| Zw.-Erg. | /schlechter pfad/zum/script.sh homebackup | |||||
| Kommentare | 1 | komplette Zeile | # echo foo bar bat | |||
| Zw.-Erg. | ||||||
| 2 | Teil einer Zeile | echo foo #bar bat | ||||
| Zw.-Erg. | echo foo | |||||
| 3 | nicht am Wortanfang | echo foo#bar bat | ||||
| Zw.-Erg. | echo foo#bar bat | |||||
| History-Expansion | 1 | nur Event-Designator | sudo !! | |||
| Zw.-Erg. | sudo ls -l /root | |||||
| Aliase | 1 | Kommando mit Quoting | \ls -ls | |||
| Zw.-Erg. | ls -ls | |||||
| 2 | Kommando ohne Quoting | ls -ls | ||||
| Zw.-Erg. | ls --color=auto -ls | |||||
| 3 | Alias mit Metazeichen | aliastest | ||||
| 1. Zw.-Erg. | echo foo ; echo bar | |||||
| echo foo ; echo bar | ||||||
| 2. Zw.-Erg. | echo foo echo bar | die aktuell berücksichtige Kommandozeile endet am Semikolon | ||||
| echo bar | ||||||
| 3. Zw.-Erg. | echo bar | (echo foo ist jetzt schon die vorherige, abgearbeitete Kommandozeile) | ||||
| Umgebungs- und Shellvariablen | 1 | nur in der Shell | a | var=foo; /bin/echo "$var" | ||
| 1. Zw.-Erg. | /bin/echo "$var" | nicht exportierte Variable $var nur in der Shell gesetzt | ||||
| /bin/echo "$var" | $var nur in der Shell gesetzt | |||||
| 2. Zw.-Erg. | /bin/echo foo | $var in der Shell gesetzt | ||||
| nur in der Shell | b | var=foo; bash -c 'echo "$var"' | ||||
| 1. Zw.-Erg. | bash -c 'echo "$var"' | nicht exportierte Variable $var nur in der Shell gesetzt | ||||
| echo "$var" | $var nur in der Elternprozess-Shell gesetzt | |||||
| 2. Zw.-Erg. | echo | $var nur in der Elternprozess-Shell gesetzt | ||||
| 2 | in der Shell exportiert | export var=foo; bash -c 'echo "$var"' | ||||
| 1. Zw.-Erg. | bash -c 'echo "$var"' | exportierte Variable $var in der Shell gesetzt | ||||
| echo "$var" | Umgebungsvariable $var vom Elternprozess geerbt | |||||
| 2. Zw.-Erg. | echo foo | Umgebungsvariable $var vom Elternprozess geerbt | ||||
| 3 | nur im neuen Prozess | a | var=foo /bin/echo "$var" | |||
| Zw.-Erg. | /bin/echo | $var nur im Environment von echo gesetzt (ohne Auswirkung) | ||||
| nur im neuen Prozess | b | var=foo bash -c 'echo "$var"' | ||||
| 1. Zw.-Erg. | bash -c 'echo "$var"' | $var nur im Environment der neuen bash gesetzt | ||||
| echo "$var" | $var nur im Environment der neuen bash gesetzt | |||||
| 2. Zw.-Erg. | echo foo | $var nur im Environment der neuen bash gesetzt | ||||
| Umleitung | 1 | Umleitung stdout | grep d:x: /etc/passwd /etc/shadow 1>/daten/passwd-auszug.txt | |||
| Zw.-Erg. | grep d:x: /etc/passwd /etc/shadow | stdout=/daten/passwd-auszug.txt | ||||
| 2 | Umleitung stdout + stderr | grep d:x: /etc/passwd /etc/shadow 1>/daten/passwd-auszug.txt 2>&1 | ||||
| 1. Zw.-Erg. | grep d:x: /etc/passwd /etc/shadow 2>&1 | stdout=/daten/passwd-auszug.txt | ||||
| grep d:x: /etc/passwd /etc/shadow 2>&1 | stdout=/daten/passwd-auszug.txt | |||||
| 2. Zw.-Erg. | grep d:x: /etc/passwd /etc/shadow | stdout=/daten/passwd-auszug.txt stderr=/daten/passwd-auszug.txt | ||||
| Brace-Expansion | 1 | einfache Klammer | a | echo foo b{ar,at,az} | ||
| Zw.-Erg. | echo foo bar bat baz | |||||
| 1 | einfache Klammer | b | mv dateiname{,.bak} | |||
| Zw.-Erg. | mv dateiname dateiname.bak | |||||
| 2 | mehrere Klammern | touch {apache2,postgresql}.service.d/{mounts,resourcelimits}.conf | ||||
| Zw.-Erg. | touch apache2.service.d/mounts.conf apache2.service.d/resourcelimits.conf postgresql.service.d/mounts.conf postgresql.service.d/resourcelimits.conf | |||||
| reservierte Wörter | 1 | Verzweigung | if true; then echo foo; fi | |||
| 1. Zw.-Erg. | true | echo foo wird ggf. später Teil der Kommandozeile | ||||
| 1 | true | |||||
| 2. Zw.-Erg. | true | (weil die Testbedingung wahr ist, wird echo foo eingefügt) | ||||
| 1 | echo foo | |||||
| 3. Zw.-Erg. | echo foo | |||||
| An dieser Stelle steht die Struktur des Kommandos fest, also nicht unbedingt der konkrete Befehl und die Argumente, aber ob es sich um ein einfaches oder zusammengesetztes Kommando (z. B. Bedingung, Schleife) handelt, wo die Eingabedaten herkommen, wo die Ausgabedaten hingehen, ob das Kommando im Vordergrund oder im Hintergrund ausgeführt wird. | ||||||
| Die folgenden Arbeitsschritte finden gleichzeitig statt | ||||||
| Tilde-Expansion | 1 | einzelne Tilde | ls -l ~ | |||
| Zw.-Erg. | ls -l /home/user | |||||
| 2 | Tilde mit Pfad | . ~/.bashrc | ||||
| Zw.-Erg. | . /home/user/.bashrc | |||||
| 3 | anderer Benutzer | echo ~otheruser | ||||
| Zw.-Erg. | echo /home/otheruser | |||||
| Parameter- und Variablen-Expansion | 1 | Parameter | echo "$0" | (in interaktiver Shell) | ||
| Zw.-Erg. | echo bash | |||||
| 2 | einfache Variable | echo "$HOME" | ||||
| Zw.-Erg. | echo /home/user | |||||
| 3 | Array-Variable | echo "${BASH_SOURCE[0]}" | (in einem Script) | |||
| Zw.-Erg. | echo ./testscript.sh | |||||
| Arithmetik-Expansion | 1 | echo $(( 2+3 )) | ||||
| Zw.-Erg. | echo 5 | |||||
| Kommando-Substitution | 1 | echo Datum: $( date +%F ) | ||||
| Zw.-Erg. | echo Datum: 2026-09-05 | |||||
| Prozess-Substitution | 1 | diff <( cd /home/user ; find . -path './.bash*' ) <( cd /home/otheruser ; find . -path './.bash*' ) | ||||
| Zw.-Erg. | diff /dev/fd/63 /dev/fd/62 | |||||
| Die folgenden Arbeitsschritte finden nacheinander statt | ||||||
| Word-Splitting | 1 | String-Variable | echo $var "$var" # var='foo bar' | |||
| Zw.-Erg. | echo foo bar foo bar | (drei Argumente) | ||||
| 2 | Aufrufparameter | bash -c 'echo $@ "$@"' bash "$var" 'bat baz' | ||||
| Zw.-Erg. | echo foo bar foo bar | (drei Argumente) | ||||
| 3 | Array-Variable | echo "${ar[*]}" "${ar[@]}" # ar=(foo bar) | (in einem Script) | |||
| Zw.-Erg. | echo foo bar foo bar | (drei Argumente) | ||||
| 4 | Kommando-Substitution ohne Quoting | echo $( date +'Datum: %Y-%m-%d Uhrzeit: %H:%M' ) | ||||
| Zw.-Erg. | echo Datum: 2026-09-05 Uhrzeit: 22:03 | (vier Argumente) | ||||
| 5 | Kommando-Substitution mit Quoting | echo "$( date +'Datum: %Y-%m-%d Uhrzeit: %H:%M' )" | ||||
| Zw.-Erg. | echo Datum: 2026-09-05 Uhrzeit: 22:03 | (ein Argument) | ||||
| Pathname-Expansion | 1 | ein Treffer | echo *.txt | |||
| Zw.-Erg. | echo README.txt | (ein Argument) | ||||
| 2 | kein Treffer | echo output-*.txt | (shopt -u nullglob) | |||
| Zw.-Erg. | echo output-*.txt | (ein Argument) | ||||
| 3 | kein Treffer (nullglob) | echo output-*.txt | (shopt -s nullglob) | |||
| Zw.-Erg. | echo | (kein Argument) | ||||
| 4 | mehrere Treffer | echo *.sh | ||||
| Zw.-Erg. | echo script.1.sh script.2.sh | (zwei Argumente) | ||||
| Quote-Removal | 1 | Backslash | echo \f\o\o\ \b\a\r\ \b\a\t\ \'\"\\ | |||
| Zw.-Erg. | echo foo bar bat '"\ | (ein Argument) | ||||
| 2 | ASCII single quotes | echo 'foo bar "bat baz"' | ||||
| Zw.-Erg. | echo foo bar "bat baz" | (ein Argument) | ||||
| 3 | ASCII double quotes | echo "foo bar \"bat baz\"" | ||||
| Zw.-Erg. | echo foo bar "bat baz" | (ein Argument) | ||||
| 4 | Quotes in Variable ohne Quoting | echo _${var}foo bar${var}_ # var='"' | ||||
| Zw.-Erg. | echo _"foo bar"_ | (kein Quoting, sondern Variablen-Expansion, deshalb nicht entfernt) | ||||
| 5 | Quotes in Variable mit Quoting | echo "_${var}foo bar${var}_" # var='"' | ||||
| 1. Zw.-Erg. | echo "_"foo bar"_" | |||||
| echo "_"foo bar"_" | ||||||
| 2. Zw.-Erg. | echo _"foo bar"_ | (ein Argument) | ||||
Syntax und Grammatik
Für das Verständnis der Kommandozeile ist es wichtig zu wissen, wie die Bash die Zeichen bewertet. Dabei wird unterschieden zwischen Zeichen(kombinationen) mit spezieller Bedeutung und normalen Zeichen:
- Zeichen, die immer einzeln wirken
- Quoting-Zeichen:
\,",' - Metacharacter (reine Worttrenner):
space,tab,newline - Metacharacter (Worttrenner mit weiterer Bedeutung):
|,&,;,(,),<,> - veraltete Form der Kommando-Substitution (jetzt:
$( )):` - Pattern-Matching:
*,?
- Quoting-Zeichen:
- Zeichen, die als komplettes Wort eine Sonderbedeutung haben
- Reservierte Wörter (einzelne Zeichen):
!,{,}
- Reservierte Wörter (einzelne Zeichen):
- Zeichen, die nur am Wortanfang eine Sonderbedeutung haben
- Kommentarzeichen:
# - Tilde-Expansion (Homeverzeichnis):
~
- Kommentarzeichen:
- Zeichen, die später in einem Wort eine Sonderbedeutung haben
- Variablenzuweisung:
=
- Variablenzuweisung:
- Zeichen, die am Anfang bestimmter Zeichenfolgen eine Sonderbedeutung haben
- Brace-Expansion:
{ - Variablen, Kommandosubstitution, Arithmetik-Expansion, ANSI-C-Quoting:
$ - einfaches Pattern-Matching:
[ - Pattern-Matching (
extglob):@,+,*,?,!
- Brace-Expansion:
- Zeichen, die später in einer bestimmten Zeichenfolge eine Sonderbedeutung haben
- Brace-Expansion:
} - einfaches Pattern-Matching:
]
- Brace-Expansion:
- Reservierte Wörter (Zeichenfolgen):
casecoprocdodoneelifelseesacfiforfunctionifinselectthenuntilwhiletime[[]] - vorbelegte Wörter (abschaltbar)
- eingebaute Befehle (builtins)
- Aliase
Die einfachste Kommandozeile besteht aus nur einem Wort und endet mit newline, ;, & oder dem Ende der Eingabe (Script oder bash -c / bash -s). Eine Bash-Eingabezeile kann mehrere Kommandos / Kommandozeilen enthalten.
Quoting
Ein Zeichen, reserviertes Wort oder ein Alias, der seine Sonderbedeutung verlieren soll, muss mit Quoting geschützt werden. Die Quoting-Zeichen werden dabei (fast immer) entfernt. Quoting schützt auch Quoting-Zeichen:
printf '%s ' \\ \' \" '"' "'" '\' "\\" $'\"\'\\'
\ ' " " ' \ \ "'\
Bei einem reservierten Wort oder einem Alias reicht es aus, dass ein einzelnes Zeichen geschützt wird. Bash kennt vier Quoting-Varianten:
- Der Backslash (
\) schützt das folgende Zeichen. - Einfache ASCII-Anführungszeichen (
') schützen ausnahmslos alle folgenden Zeichen bis zum nächsten einfachen ASCII-Anführungszeichen. Wenn man innerhalb einer derart geschützten Zeichenkette ein einfaches ASCII-Anführungszeichen benötigt, dann muss man das Quoting beenden, ein geschütztes einfaches ASCII-Anführungszeichen unmittelbar danach eingeben und dann die Zeichenkette mit erzeugtem Quoting fortsetzen:$ printf '%s\n' 'foo ${HOME} ; & '\'' bar bat < >' foo ${HOME} ; & ' bar bat < >
- Doppelte ASCII-Anführungszeichen (
") schützen alle Zeichen außer`,$und ggf.\sowie!. Der Backslash schützt`,$,",newlineund – wenn History-Expansion (die!nutzt) aktiv ist – auch!(dies ist der Ausnahmefall, in dem das Quotezeichen nicht entfernt wird, man würde also eher das Quoting unterbrechen:echo "$( date +%F ) "'foo!!bar'). - ANSI-C-Quoting erfüllt eine Doppelfunktion: Es schützt nicht nur Zeichen, sondern ermöglicht die Verwendung spezieller Zeichen über die Escape-Sequenz dieses Zeichens oder die Verwendung des Byte- oder Unicode-Werts.
$ printf '%s\n' $'foo .\nbar\tbat\x2e 1\u20105' foo . bar bat. 1‐5
der Prompt
Der Prompt ist eine typischerweise dynamische Zeichenkette, die die Bash ausgibt, wenn sie auf die Eingabe de nächsten Kommandozeile wartet. Typischerweise wird der Prompt allein von der Bash generiert, aber man kann darüber beliebige Programme aufrufen, was etwa dafür genutzt wird, darin Git-Informationen anzuzeigen, wenn das aktuelle Verzeichnis von Git verwaltet wird.
Der typische Prompt sieht so aus: user@hostname:~$ für normale User, user@hostname:~# für root
Diese Promptkonfiguration generiert die obige Ausgabe: PS1='\u@\h:\w\$ '
Es gibt fünf Prompts (Prompt-String-Variablen) in der Bash:
PS0: normalerweise ungenutzt; wird nach dem Absenden der Kommandozeile (mit Enter), aber vor der Ausführung des Kommandos angezeigtPS1: der normale PromptPS2: der Fortsetzungsprompt, falls die abgeschickte Zeile kein gültiges Ende hat, sondern weitere Eingaben erforderlich sind, etwa beiecho foo |,test -d /home ||oderfor path in "$@"; do ls -ld "$path"PS3: der Prompt des eingebauten Kommandos (builtin)selectPS4: der Prompt vor der Trace-Ausgabe (bash -x,set -x)
Zusätzlich zu den angezeigten Prompts kann man die Variable PROMPT_COMMAND für Kommandos nutzen, die vor der Anzeige des Prompts ausgeführt werden. Über ANSI-Escapesequenzen (nicht Bash-, sondern terminalspezifisch) kann man im Prompt auch unterschiedliche Farben verwenden (Ausgaben, die den Cursor nicht bewegen, sollten zwischen \[ und \] gesetzt werden, damit Readline die Ausgabe korrekt handhabt, insbesondere bei Zeilenumbrüchen).
Der ausgegebene Prompt ändert die Arbeitsweise der Bash nicht (es sind allerdings Seiteneffekte möglich); er dient zumeist nur einem besseren Erlebnis des Benutzers.
Beispielgründe, den Prompt zu ändern:
- Man möchte immer den Exit-Status des letzten Kommandos sehen; in Rot, wenn der nicht 0 ist.
- Um trivial die Laufzeit eines Kommandos zu erkennen, das man vergessen hat, mit
timezu starten, kann man die Uhrzeit zu einem Teil des Prompts machen.PS0hilft, wenn viel Zeit zwischen der Anzeige des letzten Prompts und dem Abschicken des Kommandos vergangen ist. - Um die Ausgabe eines Kommandos auch dann leicht eindeutig zu erkennen, wenn das Kommando am Ende keinen Zeilenumbruch ausgibt, kann man den Prompt mit einem Zeilenumbruch beginnen (so dass man zumeist eine Leerzeile zwischen dem Ende der Ausgabe und dem Prompt hat).
- Um Zeilenumbrüche zu vermeiden, möchte man vor der Kommandozeile einen möglichst kurzen Prompt haben, was für typische Prompts im Widerspruch zu dem Wunsch steht, dort zusätzliche Informationen unterzubringen. Eine Lösung für diese Probleme ist die Verwendung eines mehrzeiligen Prompts.
- Gerade für wenig erfahrene Shell-Nutzer mag der Wunsch bestehen, den
PS2-Prompt verständlicher zu gestalten. Auch erfahrene Benutzer mögen es als ästhetisch vorteilhaft empfinden, wenn die Eingabe in allen Zeilen an derselben horizontalen Position beginnt. - Für History-Expansion ist es hilfreich, die History-Nummer des jeweiligen Kommandos unmittelbar zu sehen, um es leichter referenzieren zu können.
- Wer immer nur auf demselben System arbeitet, mag die Anzeige des Hostnamens als Platzverschwendung ansehen. Wer zwischen einer Vielzahl von Systemen wechselt, mag davon profitieren, dass der Prompt für Systeme unterschiedlicher Kategorien (etwa Produktions- und Testsysteme) unterschiedliche Farben hat.
| Sequenz | Bedeutung | Beispiel |
|---|---|---|
\h | Hostname (vor dem ersten Punkt) | server01 |
\n | newline (Zeilenumbruch) | |
\t | Uhrzeit | 22:09:51 |
\u | Der Loginname des Benutzers | |
\v / \V | Bash-Version | 5.3 / 5.3.15 |
\w | $PWD, das aktuelle Verzeichnis (das $HOME-Präfix durch ~ abgekürzt) | ~/tmp |
\! | Die History-Nummer des Kommandos | |
\$ | für normale User $, für root # | |
\[ | Leitet die Ausgabe von Zeichen ohne Breite ein (für die korrekte Cursor-Positionierung). | |
\] | Beendet die Ausgabe von Zeichen ohne Breite (für die korrekte Cursor-Positionierung |
Die Wirkung von Prompt-Strings kann man testen, ohne den Prompt zu verändern:
$ var='\V'
$ echo "${var@P}"
5.3.15
Ein Prompt-Beispiel, das viele dieser Möglichkeiten nutzt:
$ PS0='Kommandozeile abgeschickt am \D{%Y-%m-%d} um: \t\n'
$ PS1=$'\\nec:$(\nec=$?; if [ 0 -eq $ec ]; then\nprintf %-3d $ec\nelse\nprintf \'\\[\E[31m\\]%-3d\\[\E[39m\\]\' ${ec}\nfi\n) #h:\! \D{%Y-%m-%d} \\t \\u@\\h:\\w\\nstart cmd:> '
$ PS2="cont. cmd:> "
ec:0 #h:1047 2026-09-11 05:21:03 user@hostname:/usr/lib/systemd/system start cmd:> echo foo' cont. cmd:> bar' Kommandozeile abgeschickt am 2026-09-11 um: 05:29:36 foo bar ec:0 #h:1048 2026-09-11 05:29:36 user@hostname:/usr/lib/systemd/system start cmd:> false Kommandozeile abgeschickt am 2026-09-11 um: 05:29:39 ec:1 #h:1049 2026-09-11 05:29:39 user@hostname:/usr/lib/systemd/system start cmd:>
Aliase
Aliase sind eine Textersetzung sehr früh im Auswertungsprozess, wodurch sie andere Möglichkeiten haben als beispielsweise Shellfunktionen und Variablen. In nicht-interaktiven Shells müssen Aliase erst mit shopt -s expand_aliases freigeschaltet werden. Sie können überall dort stehen, wo der Befehl eines einfachen Kommandos stehen kann. Sie werden zumeist genutzt, um häufig genutzte Kommandos abzukürzen oder mit Optionen zu ergänzen, etwa l statt ls -lh und ls statt ls --color=auto: alias l='ls -lh' ls='ls --color=auto' Dieselbe technische Vorgehensweise kann für unterschiedliche Ziele genutzt werden:
- weniger Tipparbeit, v. a. für häufige Kommandos
alias l='ls -lhL' la='ls -lahL' ll='ls -l'
- Optionen "erzwingen"
alias ls='ls --color=auto'alias cp='cp -i' mv='mv -i' rm='rm -i'
- Informationen leichter zugänglich machen
alias my-ls-sort-by-size-asc='ls -l --sort=size --reverse'alias my-ls-sort-by-size-desc='ls -l --sort=size'
Aliase sind begrenzt rekursiv. Der den Alias ersetzende Text wird selber wieder darauf überprüft, ob er mit einem Alias beginnt. Allerdings wird dasselbe Wort nicht mehrfach expandiert.
Es gibt eine alternative Möglichkeit, Aliase zu nutzen: Wenn der Ersetzungstext mit einem Leerzeichen endet, wird das auf diesen Text folgende Wort ebenfalls darauf geprüft, ob es ein Alias ist. Man kann also komplexe Argumente (lange Pfade) auf diese Weise abkürzen:
$ alias l='ls -lhL '
$ alias p-sy=/usr/lib/systemd/system
$ time var=foo l p-sy # Alias muss nicht das erste Wort sein, sondern der Befehl
$ alias l p-sy
alias l='ls -lhL '
alias p-sy='/usr/lib/systemd/system'
Aliase haben den Vorteil gegenüber Shellfunktionen, dass sie beliebigen Eingabetext generieren können, inklusive Meta-Characters, Control-Operators, Umleitungen, Umgebungsvariablen für das Kommando und reservierter Wörter. Sie haben den Nachteil, dass sie nicht mit Argumenten arbeiten können. Ein Alias hat Vorrang über eine gleichnamige Shellfunktion. Ein Alias wird mit den eingebauten Befehlen alias und unalias erstellt/überschrieben/angezeigt bzw. gelöscht.
Shellfunktionen
Shellfunktionen sind im Grunde Shellscripte, die im aktuellen Kontext definiert und ausgeführt werden. Man kann sie, wie in Programmiersprachen üblich, dafür nutzen, die Wiederholung identischen oder sehr ähnlichen Codes zu vermeiden. Wie Aliase können Shellfunktionen den Namen eines externen Kommandos überschreiben, aber im Gegensatz zu Aliasen können sie mit Argumenten arbeiten: function mkcd() { test $# -eq 1 || return 1; mkdir -p "$1" && cd "$1"; }
Editieren der Kommandozeile (Readline)
Die Bash nutzt GNU Readline, um dem Benutzer komplexe Möglichkeiten der Bearbeitung (v. a.) der Kommandozeile vor dem Abschicken (mit Enter) zu bieten:
- Positionierung des Cursors
- Löschen von Teilen der Kommandozeile
- Einfügen gelöschter Teile der Kommandozeile
- letzte Änderungen rückgängig machen
- Vervollständigung von Wörtern
- Einfügen vordefinierter Texte
Verwaltung von Vordergrund- und Hintergrundprozessen
Ein im Vordergrund laufendes einzelnes Programm oder eine solche Pipeline (zusammenfassend als Job bezeichnet), kann vom Terminal angehalten werden (typischerweise ausgelöst durch Strg-Z), was die Shell (einen neuen Prompt) in den Vordergrund bringt. Man kann dann mit den eingebauten Befehlen (builtins) jobs, fg, bg und kill
- die vorhandenen Jobs anzeigen lassen
- den angehaltenen Job
- fortsetzen (und im Vordergrund oder Hintergrund laufen lassen)
- beenden
- einen schon im Hintergrund laufenden Prozess
- im Vordergrund fortsetzen
- beenden
Kommando-Historie
Normalerweise schreibt Bash die abgeschickte Kommandozeile in die Kommando-Historie (und diese beim sauberen Beenden in eine Datei). Man kann diese Historie auf mehrere Arten nutzen:
- mit den Pfeiltasten ↑ und ↓ durchblättern
- den Inhalt durchsuchen (typischerweise mit Strg-R)
- den Inhalt anzeigen lassen (mit
historyoderfc -l) - den Inhalt bearbeiten und ggf. ausführen (
fc) - mit History-Expansion (
!) eine komplette frühere Kommandozeile oder Teile davon (verändern und) einfügen
Vervollständigung von Kommandos und Argumenten
Mit Tab kann man Kommandonamen, Kommandopfade, Argumente (v. a. Pfade im Dateisystem) und Bash-Elemente (etwa Variablennamen) vervollständigen. Die Grundfunktionen dafür sind in Bash eingebaut, man kann aber Vervollständigungs-Shellfunktionen speziell für ein Programm schreiben, die die Kommandozeilen-Optionen dieses Programms kennen und deshalb gezielt deren Einfügen ermöglichen.
Reaktion auf nicht gefundene Kommandos
Wenn die Bash ein eingegebenes Kommando nicht findet, kann sie eine Shellfunktion aufrufen, die die Distribution nutzen kann, um darauf zu reagieren. Viele große Distributionen durchsuchen dann die Paketliste und zeigen ggf. dem Benutzer an, welches Paket installiert werden kann, um dieses Kommando verfügbar zu machen.
Varianten und Konfiguration
Die Bash kann in unterschiedlichen Varianten genutzt werden, die sich dadurch unterscheiden,
- ob die Shell interaktiv ist oder als Script läuft
- ob die Shell als externes Kommando mit den Befehlen auf der Kommandozeile (
-c) oder Standardeingabe (-s) aufgerufen wird - ob die Standardeingabe der Shell ein Terminal ist (oder nicht:
cat | bash -s,echo echo foo | ssh targethost bash -s) - ob die Shell als Login-Shell aufgerufen wird (
--loginoder mit-bashals$0) - ob die Shell als restricted shell aufgerufen wird (
--restrictedoder Aufrufnamerbash) - ob die Shell im privileged mode aufgerufen wird (für SUID/SGID,
-p) - mit welchem Namen die Shell aufgerufen wird (
bash,sh,rbash) - mit welchen Argumentoptionen sie aufgerufen wird
- welche
set-Optionen gesetzt werden - welche
shopt-Optionen gesetzt werden - ob POSIX-Konformität erzwungen wird (
--posix)
Interaktivität
Eine interaktive Shell erwartet Benutzereingaben auf der Standardeingabe (abgesehen von exotischen Spezialfällen mit -i). Das ist normalerweise ein Terminal. Wenn die Bash erkennt, dass die Standardeingabe und die Standardfehlerausgabe ein Terminal sind und dass sie weder ein Dateiargument noch die -c-Argumentoption hat, betrachtet sie sich als interaktiv und produziert einen Prompt. Normalerweise weiß man, ob man sich in einer interaktiven Shell befindet, aber wenn man eine Shellfunktion ausführt, die in beiden Varianten verwendet werden kann, kann man dies mit [[ $- == *i* ]] testen.
Scripte
Man kann Shellcode in eine Datei – das Script – schreiben und dann entweder die Bash mit dieser Datei als Argument aufrufen oder diese Datei direkt als Kommando, wenn sie ausführbar ist und mit einer Shebang-Zeile (üblicherweise #! /bin/bash oder #! /usr/bin/bash) beginnt.
Eine überschaubare Menge an Shellcode, der in einer unabhängigen, frisch initialisierten Shell laufen soll, kann man mit bash -c 'echo foo; echo bar' ausführen.
Anstatt ein Script erst auf ein anderes System zu kopieren und dort über Umgebungsvariablen (ssh targethost env var=foo /path/to/script.sh) oder Argumente zu konfigurieren,
kann man auch eine Shell starten, die ihre Eingaben via SSH aus einem Script (v. a. mit einem Here-Document, in dem man die Variablen bequem setzen kann) bekommt:
{
cat <<-EOT # weil EOT nicht gequotet ist, wird ${var} ersetzt
var='${var}'
EOT
cat <<-'EOT'
# hier kann ein langes Script stehen
# der Code wird wegen des 'EOT'-Quotings literal übermittelt
...
EOT
} | ssh host bash -s
In einer nichtinteraktiven Shell gibt es keinen Prompt und wird Readline nicht verwendet. Außerdem sind einige Funktionen deaktiviert (aber aktivierbar): Aliase, history expansion und job control. Es werden keine Startdateien abgearbeitet, es sei denn in der Umgebungsvariable BASH_ENV ist ein solcher Pfad definiert oder die Bash erkennt, dass ihre Standardeingabe an einer SSH-Verbindung hängt.
(Nicht-)Login-Shell
Die Bash verhält sich anders, wenn sie als Login-Shell gestartet wird. Der Name bezieht sich darauf, dass Login-Shells zumeist von Daemonen (SSH) und ähnlichen Programmen (login auf den virtuellen Konsolen, su und sudo in einer Shell) erzeugt werden. Dies sind Programme, die mit root-Rechten laufen und deshalb die Möglichkeit haben
- eine Authentifizierung durchzuführen
- über eigene Methoden (SSH mit Schlüsseln)
- oder über die Methoden des Systems (v. a. Passwortprüfung, unter Linux via PAM)
- eine Shell als ein beliebiger Benutzer zu starten (eine
root-Shell ist nicht automatisch eine Login-Shell)
Eine Login-Shell ist dafür gedacht, der erste vom Benutzer konfigurierbare Prozess in einer Prozesshierarchie zu sein. Nur der erste Prozess muss bestimmte Einstellungen vornehmen, da diese an seine Kindprozesse vererbt werden. Deshalb ist die Initialisierung einer Login-Shell umfangreicher als die einer normalen Shell. Die bash-spezifische Initialisierung ist normalerweise für beide Arten identisch. Die Bash erkennt auf zwei Arten, dass sie als Login-Shell laufen soll:
- Der Kommandoname in
$0, der von dem aufrufenden Programm gesetzt wird, beginnt mit-(-bash,-sh,-rbash). - Sie wird mit der Argumentoption
-l/--loginaufgerufen.
Man kann mit shopt login_shell herausfinden, ob eine Shell eine Login-Shell ist.
Konfigurationsdateien
Die Bash wird durch eine Reihe von Konfigurationsdateien konfiguriert. Hierbei ist zu unterscheiden zwischen systemweiter und benutzerspezifischer Konfiguration.
Login-Shell
Eine Login-Shell liest zuerst die systemweite Konfigurationsdatei /etc/profile ein und führt die darin enthaltenen Anweisungen aus; dies kann (auch für die folgenden, benutzerspezifischen Dateien) mit der Argumentoption --noprofile unterbunden werden. Manche Distributionen inkludieren von dieser Datei aus die /etc/bash.bashrc und die ~/.bashrc. Danach prüft Bash im Benutzerverzeichnis der Reihe nach, ob ~/.bash_profile, ~/.bash_login und ~/.profile existieren, wobei nur die erste gefundene Datei abgearbeitet wird.
Wenn sich eine interaktive Login-Shell beendet oder eine nicht-interaktive Login-Shell den eingebauten Befehl (builtin) exit ausführt, wird die Datei ~/.bash_logout abgearbeitet, sofern vorhanden.
Nicht-Login-Shell
Eine interaktive Nicht-Login-Shell arbeitet die Dateien /etc/bash.bashrc und ~/.bashrc ab. Mit --norc kann dies unterbunden werden. Mit --rcfile kann man eine andere benutzerspezifische Konfigurationsdatei festlegen.
In einer nichtinteraktiven Shell werden keine Startdateien abgearbeitet, es sei denn in der Umgebungsvariable BASH_ENV ist ein solcher Pfad definiert oder die Bash erkennt, dass ihre Standardeingabe an einer SSH-Verbindung hängt (oder an einer anderen Netzwerkverbindung wie rsh, was aber heute nicht mehr relevant ist). Im SSH-Fall werden die Dateien /etc/bash.bashrc (die üblicherweise prüft, ob /etc/profile inkludiert wurde und dies ggf. nachholt) und ~/.bashrc abgearbeitet, allerdings wird BASH_ENV dort nicht beachtet.
| Login-Shell | Interaktivität | SSH-stdin | direkt inkludiert | oftmals indirekt inkludiert | Bemerkungen |
|---|---|---|---|---|---|
| Login-Shell | interaktiv | kein SSH | /etc/profile + (~/.bash_profile oder ~/.bash_login oder ~/.profile) | /etc/bash.bashrc ~/.bashrc | --noprofile |
| SSH | /etc/profile + (~/.bash_profile oder ~/.bash_login oder ~/.profile) | /etc/bash.bashrc ~/.bashrc | --noprofile | ||
| nichtinteraktiv | kein SSH | /etc/profile + (~/.bash_profile oder ~/.bash_login oder ~/.profile) | /etc/bash.bashrc ~/.bashrc | --noprofile | |
| SSH | /etc/profile + (~/.bash_profile oder ~/.bash_login oder ~/.profile) | /etc/bash.bashrc ~/.bashrc | --noprofile | ||
| Nicht-Login-Shell | interaktiv | kein SSH | /etc/bash.bashrc ~/.bashrc | --norc | |
| SSH | /etc/bash.bashrc ~/.bashrc | /etc/profile | --norc | ||
| nichtinteraktiv | kein SSH | $BASH_ENV | |||
| SSH | /etc/bash.bashrc ~/.bashrc |
Shell-Optionen: set und shopt
Bash hat zwei Kategorien von Konfigurationsmöglichkeiten, die von unterschiedlichen eingebauten Befehlen (builtins) verwaltet werden: set und shopt. Beide können auch beim Aufruf der Shell über Argumentoptionen konfiguriert werden.
set
Die set-Konfigurationen kann man auf drei Arten ändern: Jede Option hat einen Namen (etwa noglob), fast jede auch einen einzelnen Buchstaben für eine Kommandozeilenoption, sowohl für set als auch für den Aufruf der Bash selber (in diesem Fall -f).
set -f / set -o noglob setzt die Option, set +f / set +o noglob löscht sie. Gesetzte Optionen (denen ein Buchstabe zugeordnet ist) werden der Variable $- hinzugefügt (und ggf. dort herausgelöscht). Alternativ kann man diese Optionen über die Umgebungsvariable SHELLOPTS (vor dem Start der Bash) setzen.
$ bash -c 'echo -n /e?c; echo " $-"'
/etc hBc
$ bash -c 'set -f ; echo -n /e?c; echo " $-"'
/e?c fhBc
$ bash -f -c 'echo -n /e?c; echo " $-"'
/e?c fhBc
Die aktuelle Konfiguration kann mit set -o angezeigt werden. Mit set +o bekommt man eine Ausgabe, die als Kommandoliste verwendet werden kann, um den aktuellen Zustand wiederherzustellen (errexit muss allerdings gesondert behandelt werden, weil es nicht verlässlich in eine Subshell vererbt wird):
# aktuellen Stand sichern
$ set_restore="$( set +o )"
set +o | grep -q 'set -o errexit' && set_restore+=$'\nset -o errexit\n'
# Konfiguration ändern
$ set -o allexport
# gesicherten Stand wiederherstellen
$ eval "$set_restore" 2>/dev/null # ggf. irrelevante Fehlermeldungen ignorieren
In einer Shellfunktion kann man mit local - die Änderung von set-Optionen auf den Bereich der Funktion begrenzen.
| Name | Buchstabe | Bedeutung |
|---|---|---|
| allexport | a | Variablen, die gesetzt werden, werden automatisch exportiert (export var=foo). |
| braceexpand | B | Klammerexpansion ist aktiv (echo var{1,2,3}). |
| emacs | An Emacs angelehnte Steuerung der Kommandozeilen-Editierung (Readline). | |
| errexit | e | Die Shell beendet sich, wenn eine Pipeline sich mit einem Fehler-Exit-Code beendet (ggf. nach Umkehrung durch !; es sei denn, dieser Exit-Code wird von if, while, until, && oder || ausgewertet). |
| errtrace | E | Ein ERR-trap wird an Shell-Funktionen, Kommando-Substitutionen und Subshells vererbt. |
| functrace | T | Die DEBUG- und RETURN-traps werden an Shell-Funktionen, Kommando-Substitutionen und Subshells vererbt. |
| hashall | h | Den Pfad von über $PATH gefundenen Kommandos speichern (entspricht dem builtin hash). |
| histexpand | H | History-Expansion ist aktiv (echo !!). |
| history | Kommandozeilen-History ist aktiv (Pfeiltasten nach oben/unten; history). | |
| ignoreeof | Setzt die Anzahl der nötigen aufeinanderfolgenden EOF-Zeichen, um die Shell zu beenden, auf den Wert von IGNOREEOF. | |
| keyword | k | var=foo-Zuweisungen für Umgebungsvariablen können nicht nur vor, sondern auch hinter dem Kommando stehen. |
| monitor | m | Job-Control ist aktiv (Steuerung von Hintergrundprozessen). |
| noclobber | C | Bestehende Dateien können nicht per Ausgabeumleitung mit > überschrieben werden, nur mit >|. |
| noexec | n | Debugging von Scripten: Verhindert die Ausführung der Kommandos (führt aber die Syntaxprüfung durch). |
| noglob | f | Pathname-Expansion ist deaktiviert (echo /e?c). |
| notify | b | Das Beenden eines Hintergrundjobs wird sofort gemeldet (kann mit der Ausgabe des laufenden Vordergrundkommandos überlappen). |
| nounset | u | Der Zugriff auf nicht gesetzte Variablen (auch als Array-Index) ist ein Fehler; gilt nicht für $*, $@, ${var[*]} und ${var[@]}. |
| onecmd | t | Die Shell beendet sich nach der Ausführung des ersten Kommandos. |
| physical | P | Ändert das cd-Standardverhalten von cd -L auf cd -P. |
| pipefail | Der Exit-Code einer Pipeline ist nicht mehr zwangsläufig der des letzten Kommandos, sondern ggf. der des am weitesten rechts stehenden, der nicht Null ist. | |
| posix | POSIX-Mode ist aktiv; dies ändert diverse Standardkonfigurationen. | |
| privileged | p | Primär beim Start der Bash sinnvoll. Ignoriert die Übernahme diverser Einstellungen aus Umgebungsvariablen. Verhindert bei einem Bash-Prozess mit abweichenden effektiven und realen UIDs/GIDs,(z. B. durch SUID- und/oder SGID), dass die effektive ID auf die reale zurückgesetzt wird. |
| verbose | v | Debughilfe; gibt die eingelesenen Kommandozeilen vor der Ausführung aus. |
| vi | An vi angelehnte Steuerung der Kommandozeilen-Editierung (Readline). | |
| xtrace | x | Debughilfe; gibt die expandierten Kommandos vor der Ausführung auf stderr aus. |
shopt
Die shopt-Konfigurationen kann man auf drei Arten ändern: Jede Option hat einen Namen (etwa extglob), anders als bei set aber keine Buchstaben-Abkürzung. In der laufenden Shell kann man eine Option mit shopt -s extglob setzen und mit shopt -u extglob löschen. Die Bash-Argumentoption ist -O extglob für das Setzen und +O extglob für das Löschen einer Option. Alternativ kann man diese Optionen über die Umgebungsvariable BASHOPTS (vor dem Start der Bash) setzen.
Mit shopt / shopt extglob nullglob kann man sich den Status für alle oder einzelne Konfigurationen ansehen; mit shopt -s nur den der aktiven, mit shopt -u nur die inaktiven. Anders als bei set kann man für shopt-Optionen direkt abfragen, ob sie (einzeln oder als Gruppe) aktiv sind: shopt -q extglob, shopt -q extglob nullglob. Sichern und wiederherstellen kann man den aktuellen Stand mit:
# aktuellen Stand sichern
$ shopt_restore="$( shopt -p )"
# Konfiguration ändern
$ shopt -s nullglob
# gesicherten Stand wiederherstellen
$ eval "$shopt_restore"
| Name | Bedeutung |
|---|---|
| compatxy | z. B. compat40; stellt das Bash-Verhalten auf Kompatibilität mit einer bestimmten Version ein |
| dotglob | Pathname-Expansion mit * inkludiert Namen, die mit einem Punkt beginnen (außer . und ..). |
| expand_aliases | Aliase werden expandiert; standardmäßig in interaktiven Shells aktiv. |
| extdebug | Debugfunktionen, v. a. über DEBUG-, RETURN- und ERR-Traps. |
| extglob | Erweitert die Möglichkeiten von Pattern-Matching (und damit auch Pathname-Expansion). |
| failglob | Wenn ein Muster bei Pathname-Expansion keinen Treffer liefert, wird das Kommando nicht ausgeführt. |
| globasciiranges | Ändert die Definition der Pattern-Matching-Bereiche ([a-z]) von dem Locale auf C. |
| globskipdots | .* trifft . und .. nicht. |
| globstar | ** und **/ erfassen in Pathname-Expansion alle Unterverzeichnisse. |
| histappend | Die Kommandozeilen der laufenden Shell werden der History-Datei hinzugefügt, anstatt sie zu überschreiben. |
| histreedit | Eine fehlgeschlagene History-Expansion kann mittels Readline bearbeitet werden. |
| histverify | Bei Verwendung von History-Expansion wird die Kommandozeile nicht gleich ausgeführt, sondern nach Ersetzung der History-Referenzen angezeigt und muss mit Enter bestätigt werden. |
| hostcomplete | Readline versucht Hostname-Completion bei Wörtern, die ein @ enthalten. |
| huponexit | Bash schickt ein SIGHUP-Signal an alle Jobs, wenn eine interaktive Login-Shell sich beendet. |
| inherit_errexit | Die Shelloption errexit wird an Command-Substitution-Subshells vererbt. |
| interactive_comments | # wirkt auch in einer interaktiven Shell als Kommentarzeichen. |
| lastpipe | Wenn Job-Control deaktiviert ist, läuft das letzte Element einer Pipeline in der aktuellen Shell-Umgebung statt in einer Subshell. |
| login_shell | Nur lesbar; die Bash zeigt über den Status dieser "Option" an, ob es sich um eine Login-Shell handelt. |
| nocaseglob | Pathname-Expansion ignoriert Groß-Klein-Schreibung. |
| nocasematch | Pattern-Matching, das nicht Pathname-Expansion ist (case und [[ ]]), ignoriert Groß-Klein-Schreibung. |
| nullglob | Pathname-Expansion-Muster, die keine Treffer generieren, werden aus der Kommandozeile entfernt. |
| progcomp | Aktiviert Programmable-Completion. |
| promptvars | Aktiviert diverse Expansionen und Quote-Removal für den Prompt. |
| restricted_shell | Nur lesbar; die Bash zeigt über den Status dieser "Option" an, ob es sich um eine Restricted-Shell handelt. |
| sourcepath | Der eingebaute Befehl (builtin) . / source sucht im $PATH nach der übergebenen Datei, wenn die Option -p nicht verwendet wird. |
| xpg_echo | Der eingebaute Befehl (builtin) echo wertet Backslash-Escapesequenzen standardmäßig aus. |
Subshell
Eine Subshell ist ein Kindprozess von bash, der als diese Shell weiterläuft, also sich nicht mittels execve() neu initialisiert (typischerweise als ein anderes externes Kommando). Wenn bash ausdrücklich einen neuen bash-Prozess startet,
$ bash -c 'echo foo'
dann ist dies keine Subshell, sondern nur „zufällig“ ein bash-Prozess, der aber genauso erzeugt wurde und genauso unabhängig von seiner Elternshell ist, wie jedes andere externe Kommando.
Da die Subshell eine (fast) exakte Kopie ist, die den normalen Shell-Initialisierungsprozess nicht durchläuft, hat man Zugriff auf dieselben Variablenwerte wie in der Hauptshell. Man kann eine Subshell auf folgenden Wegen erkennen:
- Die Variable
$BASH_SUBSHELLwird inkrementiert. - In einer normalen Shell sind
$$und$BASHPIDidentisch. In einer Subshell enthält$$die Prozess-ID der aufrufenden Shell; dies ist zumeist die Elternshell, kann aber auch eine frühere Elterngeneration sein:echo $BASHPID $$ ; ( echo $BASHPID $$ ; ( echo $BASHPID $$ ) )
Eine Subshell kann die Elternshell nicht beeinflussen, deshalb kann man sie explizit nutzen, um keine Rücksicht darauf nehmen zu müssen, dass man die Shellkonfiguration ändert:
$ if ( shopt -s nullglob extglob ; set -- foo_@(xy|yz)_bar.txt ; test $# -eq 0) ; then echo 'ERROR: Datei nicht vorhanden' fi
Man kann Subshells auch nutzen, um Kommandos zusammenzufassen, etwa für die Ein- oder Ausgabeumleitung, aber dafür benötigt man keine Subshell, das kann man genauso mit einem group command erreichen:
$ ( echo foo ; echo bar ; echo bat ) | grep a
$ { echo foo ; echo bar ; echo bat;} | grep a
In manchen Situationen erzeugt bash automatisch Subshells:
- Pipelines
grep -v '^\s*#' *.conf | grep user- Die Elemente einer Pipeline laufen gleichzeitig, es müssen also zwangsläufig Kindprozesse erzeugt werden. Bevor die eigentlichen Prozesse der Pipeline-Elemente gestartet werden, werden die Dateideskriptoren 0 (stdin) und 1 (stdout) der Kindprozesse so miteinander verbunden, wie die Pipelinestruktur dies vorsieht.
- Mit
shopt -s lastpipe(wenn job control deaktiviert ist, also vor allem in Shellscripten) kann man das letzte Element einer Pipeline in der Hauptshell laufen lassen (was nur bei internen Shellkommandos (eingebauten Befehlen (builtins); Funktionen; compound commands) ein Unterschied ist).
- command substitution
$ echo "Das heutige Datum ist: $( date )"
- process substitution
diff <( grep -v '^\s*#' file-1 ) <( grep -v '^\s*#' file-2 )
- Hintergrundprozesse
long_running_command &
- Koprozess-Hintergrundprozesse
coproc grep foo
Subshells können durchaus hinderlich sein:
# in einer Shell ohne lastpipe $ echo foo | read line $ echo "$line" # keine Ausgabe
Das read line wird in einer Subshell ausgeführt. Dort wird die Variable $line erstellt, aber dies erzeugt keine Variable in der Hauptshell.
Eingebaute Befehle
Die Bash enthält zahlreiche eingebaute Befehle und reservierte Wörter. Zu den eingebauten Befehlen zählen beispielsweise alias, cd, echo, exit, help, kill, logout oder pwd und zu den reservierten Wörtern zählen die für die Bash-Programmierung nötigen Schlüsselwörter wie case, for, function oder while.
Pattern matching
Bash bietet drei Varianten von Pattern Matching:
- Wildcards (Platzhalter)
extglob(extended globbing)- erweiterte reguläre Ausdrücke
Wildcards
Als Wildcards verwendet die Bash
*für beliebig viele Zeichen (also auch keines)**nur pathname expansion: alle Dateien, Verzeichnisse und deren Unterverzeichnisse**/nur pathname expansion: alle Verzeichnisse und deren Unterverzeichnisse?für genau ein beliebiges Zeichen[...]für genau eins der zwischen den eckigen Klammern aufgelisteten Zeichen[^...]/[!...]für genau ein Zeichen, das nicht zwischen den eckigen Klammern aufgelistet ist[a-eF-J0-4]für genau eins der Zeichen aus einem Zeichenbereich, der durch das Locale (Sortierreihenfolge) oder die Shelloptionglobasciiranges(aufC) festgelegt wird[[:class:]]für genau eins der Zeichen aus einer Zeichenklasse[[=,=]]für genau eins der Zeichen aus der Äquivalenzklasse (z. B.,,;,.) des angegebenen Zeichens
Die verfügbaren Zeichenklassen sind dieselben wie bei regulären Ausdrücken: [:alnum:], [:alpha:], [:ascii:], [:blank:], [:cntrl:], [:digit:], [:graph:], [:lower:], [:print:], [:punct:], [:space:], [:upper:], [:word:], [:xdigit:]
Extended globbing
Mit shopt -s extglob kann man extended globbing aktivieren. Diese Funktion stellt fünf Klammerausdrücke zur Verfügung, die einen oder mehrere alternative Muster enthalten, die wiederum Wildcards und Klammerausdrücke enthalten können: @(foo|bar) passt auf entweder foo oder bar.
| Klammerausdruck | Bedeutung |
|---|---|
?(foo) | Passt auf kein oder genau ein Vorkommen von foo. |
*(foo) | Passt auf beliebig viele Vorkommen von foo (inklusive keins). |
+(foo) | Passt auf mindestens ein Vorkommen von foo. |
@(foo) | Passt auf genau ein Vorkommen von foo. |
!(foo) | Passt auf alles, was nicht foo ist. Achtung: Dies hat nicht die Mächtigkeit der negative Vorwärtssuche von PCRE (^(?!foo).*): !(foo)* passt auf foo! Der Klammerausdruck passt dabei auf die leere Zeichenkette. Vor und nach dem negierenden Klammerausdruck darf sinnvollerweise kein * stehen; der muss in die Klammer. Die Bash unterstützt keine PCRE. |
Wenn sich ein Klammermuster wiederholen darf (*(foo), +(foo)), dann bezieht sich die Wiederholung auf das Muster, nicht auf das Zeichen, das beim ersten Mal getroffen wurde! Der Klammerausdruck kann also eine Zeichenkette aus unterschiedlichen Zeichen treffen.
Reguläre Ausdrücke
Die Bash unterstützt erweiterte reguläre Ausdrücke. Diese stehen nur in dieser Form zur Verfügung:
[[ $zeichenkette =~ $pattern ]], z. B. [[ $zeichenkette =~ ^.*\.txt~?$ ]]
Anwendungsvarianten von Wildcards und extended globbing
Beide Varianten können sowohl in abstraktem pattern matching mit beliebigen Zeichenketten als auch mit pathname expansion verwendet werden.
Beliebige Zeichenketten können mit dem [[ ]]-Kommando getestet werden:
[[ foo == f[o]? ]]/[[ foo != f[o]? ]][[ $dateiname == *.conf ]]/[[ $dateiname != *.conf ]][[ $systemd_unit_name == *.@(service|target|mount|timer) ]]
Pathname expansion ist die allgemein bekannte Verwendung von Wildcards zum Erfassen aller oder nur bestimmter real existierender Dateinamen / Dateipfade. Vor Bash 4.0 war pathname expansion nur in einer bestimmten Verzeichnisebene möglich (keine Rekursion). Inzwischen kann man mit shopt -s globstar das rekursive Erfassen aller Unterverzeichnisse mittels ** aktivieren.
Beispiele für pathname expansion
Für die folgenden Beispiele werden mit den Kommandos
mkdir -p subdir/subsubdir ; touch datei datei{1..3} datei{,1,2}.{txt,conf} archiv.tar subdir/{,subsubdir/}archiv.tar
diese Dateien und Unterverzeichnisse angelegt:
datei datei1 datei2 datei3 datei.txt datei.conf datei1.txt datei1.conf datei2.txt datei2.conf archiv.tar subdir/archiv.tar subdir/subsubdir/archiv.tar
| pattern | getroffene Dateinamen / Pfade | Bemerkungen |
|---|---|---|
datei* | datei datei1 datei1.conf datei1.txt datei2 datei2.conf datei2.txt datei3 datei.conf datei.txt | auch datei ohne Nummer |
datei? | datei1 datei2 datei3 | genau ein Zeichen |
datei[1-5]* | datei1 datei1.conf datei1.txt datei2 datei2.conf datei2.txt datei3 | Zeichenbereich |
datei[.2]* | datei2 datei2.conf datei2.txt datei.conf datei.txt | genau ein enthaltenes Zeichen |
datei[^12345]* | datei.conf datei.txt | genau ein nicht enthaltenes Zeichen |
** | archiv.tar datei datei1 datei1.conf datei1.txt datei2 datei2.conf datei2.txt datei3 datei.conf datei.txt subdir subdir/archiv.tar subdir/subsubdir subdir/subsubdir/archiv.tar | |
**/ | subdir/ subdir/subsubdir/ | |
**/*.?[!x]? | archiv.tar subdir/archiv.tar subdir/subsubdir/archiv.tar | Datei-Endung mit drei Zeichen, kein x in der Mitte |
**/*@(.tar|.txt) | archiv.tar datei1.txt datei2.txt datei.txt subdir/archiv.tar subdir/subsubdir/archiv.tar | extended globbing |
datei?(?).* | datei1.conf datei1.txt datei2.conf datei2.txt datei.conf datei.txt | vor dem Punkt ein optionales beliebiges Zeichen |
datei+([.[:digit:]])*t | datei1.txt datei2.txt datei.txt | mindestens einmal das Klammermuster |
Verhalten bei fehlenden Treffern
Die Shelloption nullglob (shopt -s nullglob) steuert die Reaktion der Bash, wenn ein Muster bei pathname expansion keine Treffer generiert. Standardmäßig ist diese Option abgeschaltet, dann bleibt das Muster unverändert stehen, was selten sinnvoll ist, wenn pathname expansion beabsichtigt war. Es funktioniert, wenn eine Zeichenkette mit Wildcards als Argument übergeben werden soll, etwa in
find . -type f -name *.conf
Aber solche Aufrufe sollte man grundsätzlich vermeiden und immer mit Quoting arbeiten (-name '*.conf'). Wenn die Shelloption nullglob angeschaltet ist, wird das Muster komplett aus der Kommandozeile entfernt. Auch das kann unerwünscht sein. Es gibt zwei Möglichkeiten, den Fall fehlender Treffer zu handhaben:
- explizit:
if ( shopt -s nullglob ; set -- *pattern* ; (( $# )) ) ; then ... - Man setzt die Shelloption
failglob(shopt -s failglob), dann führt die Shell die Kommandozeile gar nicht erst aus, wenn eins der Muster in der Kommandozeile keine Treffer generiert:$ echo *nomatch* bash: Keine Entsprechung: *nomatch* # oder if ( shopt -s failglob; : *nomatch* ) 2>/dev/null ; then ...
Man kann nullglob auch ohne „echtes“ pattern matching nutzen, um Einträge aus einer Argumentliste zu entfernen, wenn die entsprechende Datei nicht existiert:
$ touch datei_existiert
$ echo datei_existier[t] datei_existiert_nich[t]
datei_existiert
$ touch datei_existiert_nicht
$ echo datei_existier[t] datei_existiert_nich[t]
datei_existiert datei_existiert_nicht
Groß-Klein-Schreibung und Globbing-Filter
Mit der Shelloption nocaseglob (shopt -s nocaseglob) kann man dafür sorgen, dass die Groß-Klein-Schreibung bei der Trefferprüfung ignoriert wird.
Mit der Shell-Variable GLOBIGNORE kann man Treffer herausfiltern. Ist diese Variable nicht leer, wird die Shelloption dotglob automatisch gesetzt und werden die Treffer . und .. immer ignoriert.
versteckte Dateien
Pathname expansion erfasst standardmäßig keine versteckten Dateinamen; das sind diejenigen, die mit einem Punkt . beginnen. Es gibt zwei Möglichkeiten, diese Dateien zu erreichen:
- Man adressiert sie direkt, indem man das Suchmuster (innerhalb des Verzeichnisses) mit einem Punkt beginnen lässt. Das hat den Nachteil, dass man nur diejenigen Einträge erfasst, die mit einem Punkt beginnen.
- Mit der Shelloption
dotglob(shopt -s dotglob) erfasst * auch versteckte Dateien – mit Ausnahme von.und.., die man also ggf. hinzufügen muss (typischerweise will man die gar nicht in den Ergebnissen haben):ls -ld . .. *
Die Shelloption globskipdots (shopt -s globskipdots) sorgt dafür, dass bei Verwendung des Musters .* die Verzeichnisse . und .. nicht getroffen werden.
Ein- und Ausgabe-Deskriptoren
Konsolenprogramme – auch die Bash – kommunizieren nicht nur beim expliziten Arbeiten mit Dateien mit Hilfe sogenannter Dateideskriptoren. Das sind ganze Zahlen, beginnend bei Null. Die Zahlen 0, 1 und 2 sind dabei vorbelegt:
0:stdin, die Standardeingabe; normalerweise die Tastatur bzw. das (Pseudo-)Terminal (das seine Eingaben auch auf anderem Weg erhalten kann, etwa per SSH).1:stdout, die Standardausgabe, normalerweise das Terminal oder eine Pipeline (verbunden mit der Standardeingabe eines anderen Programms)2:stderr, die Standardfehlerausgabe, normalerweise ebenfalls das Terminal
Ein Programm kann zwar prüfen, ob ein I/O-Deskriptor mit einem Terminal verbunden ist (bash: if [ -t 1 ]; then), aber normalerweise muss ein Programm das nicht wissen und macht in allen Fällen dasselbe. Häufigste Ausnahme ist die Formatierung: Auf dem Terminal wird in Farbe ausgegeben, aber in einer Pipeline oder Datei würden die ANSI-Escape-Sequenzen zumeist nur stören, deshalb wird dorthin unformatiert geschrieben. Wenn man wissen will, welches das aktuelle Terminal ist, kann man den (nicht bash-spezifischen) Befehl tty verwenden:
$ tty
/dev/pts/92
Falls ein Programm von der Standardeingabe lesen soll, aber zwingend eine Qualldatei erwartet, kann man sich mit cat datei | programm oder (unter Linux) mit programm /dev/stdin behelfen.
Umleitung
Die häufigste Form der expliziten I/O-Konfiguration ist die Umleitung der Standard-Deskriptoren (innerhalb einer Pipeline kümmert sich die Bash automatisch um die nötigen Anpassungen). Die Umleitungsanweisungen können an beliebiger Stelle in der Kommandozeile stehen. Die Bash wertet sie aus und „entfernt“ sie, bevor das Kommando gestartet wird. Das Kommando sieht die Umleitungen nicht als eigenes Argument. Zwischen dem Umleitungszeichen und einem folgenden Dateinamen kann ein Leerzeichen stehen, muss aber nicht. Bei der Umleitung auf einen bestehenden Deskriptor (>&2) darf kein Leerzeichen folgen.
- Wegwerfen der Standardfehlerausgabe. Oftmals will man die Fehlermeldungen nicht sehen:
$ find /etc/selinux -name '*.LOCK' /etc/selinux/targeted/semanage.read.LOCK /etc/selinux/targeted/semanage.trans.LOCK find: ‘/etc/selinux/targeted/active’: Keine Berechtigung find: ‘/etc/selinux/final’: Keine Berechtigung $ find /etc/selinux -name '*.LOCK' 2>/dev/null /etc/selinux/targeted/semanage.read.LOCK /etc/selinux/targeted/semanage.trans.LOCK
- Umleiten der Standardausgabe in eine Datei
$ find /etc -type f 2>/dev/null >~/find-etc.txt
- Umleiten der Standardeingabe auf eine Datei (d. h. Lesen aus der Datei statt vom Terminal)
$ while IFS= read -r line; do printf '%s: %s\n' "$inputfile" "$line" ; done < "$inputfile"
- Umleiten eines Deskriptors auf den anderen, z. B. die
stdout-Ausgabe eines Kommandos aufstderrlenken:$ echo foo 1>&2 # oder: echo foo >&2
Bei der Eingabeumleitung ist der Standard-Deskriptor 0, bei der Ausgabeumleitung 1. Deshalb kann man statt 0< einfach < verwenden und statt 1> einfach >.
Man kann beide Ausgaben gleichzeitig umleiten:
- beide wegwerfen:
- beide explizit (Reihenfolge ist egal):
$ bash -c 'echo foo >&1 ; echo bar >&2' >/dev/null 2>/dev/null
- beide Deskriptoren gleichzeitig umleiten:
$ bash -c 'echo foo >&1 ; echo bar >&2' &>/dev/null
- einen Deskriptor explizit umleiten, den anderen auf den ersten kopieren; hier ist die Reihenfolge entscheidend!
- funktioniert:
$ bash -c 'echo foo >&1 ; echo bar >&2' >/dev/null 2>&1 - funktioniert nicht:
$ bash -c 'echo foo >&1 ; echo bar >&2' 2>&1 >/dev/null
- funktioniert:
- beide explizit (Reihenfolge ist egal):
- getrennt umleiten (Reihenfolge ist egal):
$ find /etc -type f 2>/dev/null >~/find-etc.txt
Die meisten Programme, die eine Eingabe bearbeiten können (grep, awk, sed, cut, cat, nl, fold, …) können direkt mit einer Datei aufgerufen werden, es gibt also selten Grund für grep < file statt grep file. Allerdings schafft die Umleitung ein hohes Maß an Gewissheit, dass das Programm die Datei nicht verändert.
Mit >> statt > kann die Ausgabe an eine bestehende Datei angehängt werden, anstatt die Datei zu überschreiben.
Schutz vor versehentlichem Überschreiben
Bei einer Ausgabeumleitung wird die Datei angelegt, wenn sie noch nicht existiert. Eine bestehende Datei wird (ohne Warnung) überschrieben. Wenn man eine Datei nicht löschen, sondern nur ihre Größe auf Null setzen möchte, kann man das also hiermit tun:
$ echo foo >datei
$ ls -l datei
-rw-r----- 1 hl hl 4 19. Aug 02:07 datei # Größe 4 Byte für foo und den Zeilenumbruch am Ende
$ >datei
$ ls -l datei
-rw-r----- 1 hl hl 0 19. Aug 02:09 datei
Wenn man nicht riskieren möchte, dass man eine bestehende Datei versehentlich überschreibt, kann man dies mit der Shelloption noclobber (set -o noclobber) unterbinden.
$ >datei
bash: datei: Kann existierende Datei nicht überschreiben
Man kann dann entweder vor der Umleitung die Datei löschen oder statt > den Umleitungsoperator >| verwenden.
zusätzliche Deskriptoren
Anstatt pro Kommando einen der Standard-Deskriptoren umzuleiten, kann man dauerhaft weitere anlegen (bis 9 problemlos, bei größeren Werten sollte man prüfen (unter Linux mit test -e /proc/self/fd/42), ob der Deskriptor frei ist, oder die bash mit {varname}>datei einen freien (oberhalb von 9) auswählen lassen). Neue Deskriptoren legt man mit dem eingebauten Shellkommando (builtin) exec an.
4zum Lesen öffnenexec 4<datei_readread line <&4
5zum Schreiben öffnenexec 5>datei_writeecho foo >&5echo bar >&5 # überschreibt die Datei trotz > nicht, weil der Deskriptor nicht neu geöffnet wird
6zum Anhängen öffnenexec 6>>datei_appendecho foo >&6 # kein >> erforderlich, weil der Deskriptor schon für Anhängen geöffnet istecho bar >&6
7zum Lesen und Schreiben öffnenexec 7<>datei_read_writeread line <&7echo bar >&7
- beliebigen freien Deskriptor (größer 9) zum Lesen öffnen
exec {fdname}<datei_readread line <&${fdname}
Einen Deskriptor gleichzeitig zum Lesen und Schreiben zu öffnen ist nicht immer sinnvoll, weil jeder Deskriptor nur eine gemeinsame Position für Lesen und Schreiben hat und deshalb beide Zugriffsarten einander in die Quere kommen können. In diesen Fällen öffnet man unterschiedliche Deskriptoren explizit (keine 5>&4-Deskriptor-Duplikation) auf dieselbe Datei, einen zum Lesen, den anderen zum Schreiben. Schließen kann man einen Deskriptor mit exec 10>&-. Die Variante echo foo {fdname}>datei_write erzeugt auch ohne exec einen dauerhaften Deskriptor (sofern die Shelloption varredir_close nicht gesetzt ist: shopt -s varredir_close).
Unter Linux kann man sich das Ergebnis der unterschiedlichen Varianten so ansehen:
$ ls -l /proc/self/fd
insgesamt 0
lrwx------ 1 hl hl 64 19. Aug 03:04 0 -> /dev/pts/92
lrwx------ 1 hl hl 64 19. Aug 03:04 1 -> /dev/pts/92
lrwx------ 1 hl hl 64 19. Aug 03:04 2 -> /dev/pts/92
lr-x------ 1 hl hl 64 19. Aug 03:04 3 -> /proc/1375879/fd
lr-x------ 1 hl hl 64 19. Aug 03:04 4 -> /.../datei_read
l-wx------ 1 hl hl 64 19. Aug 03:04 5 -> /.../datei_write
l-wx------ 1 hl hl 64 19. Aug 03:04 6 -> /.../datei_append
lrwx------ 1 hl hl 64 19. Aug 03:04 7 -> /.../datei_read_write
$ head /proc/self/fdinfo/5 /proc/self/fdinfo/6
== > /proc/self/fdinfo/5 < ==
pos: 0
flags: 0100001
mnt_id: 114
ino: 19764409
== > /proc/self/fdinfo/6 < ==
pos: 0
flags: 0102001
mnt_id: 114
ino: 19764578
Der Unterschied zwischen > und >> zeigt sich nicht in den /proc-Zugriffsrechten, sondern erst in den unterschiedlichen (oktalen, nicht zwingend auf allen Linux-Systemen identischen) Flags in /proc/self/fdinfo:
| Bit | Bedeutung |
|---|---|
0000001 | O_WRONLY |
0002000 | O_APPEND |
0100000 | O_LARGEFILE |
Here-Documents
Ein Here-Document ist eine Komfort-Funktion zur besseren Handhabung längerer, insbesondere mehrzeiliger Texte. Die Bash behandelt alle Zeilen zwischen der Zeile mit dem Startoperator und der Start-Zeichenkette und der Zeile mit der Ende-Zeichenkette als Inhalt, der auf die Standardeingabe des jeweiligen Kommandos geschrieben wird, so wie cat datei | cmd dies täte, wenn der Text in einer gesonderten Datei stünde. Allerdings gibt es zwei Kategorien für unterschiedliche Betriebsarten:
- Quoting:
- Wenn das nach dem Startoperator
<<festgelegte Wort gequotet ist, dann ist die Ende-Zeichenkette (häufigEOTfür end of text) das Wort ohne die Quotezeichen und wird der Inhalt literal ausgegeben. - Bei einer nicht gequoteten Start-Zeichenkette Werden Parameter-Expansion (
${HOME}), Command-Substitution ($( date )) und Arithmetic-Expansion ($((2+3))) auf den Inhalt angewendet, und ein Backslash am Zeilenende und der folgende Zeilenumbruch werden entfernt, was die Ende-Zeichenkette in der nächsten Zeile aufhebt.\,$und`können mit\gequotet werden, um sicherzugehen, dass sie literal verwendet werden:$ cat <<EOT foo ${HOME} bar $( date +%F ) bat $(( 2+5 )) \$ \` \\ _\ bar bat\ EOT EOT # Ausgabe ist: foo /home/hl bar 2026-08-20 bat 7 $ ` \ _bar batEOT $ cat <<'E O T' foo ${HOME} bar $( date +%F ) bat $(( 2+5 )) \$ \` \\ _\ bar bat\ E O T E O T # Ausgabe ist: foo ${HOME} bar $( date +%F ) bat $(( 2+5 )) \$ \` \\ _\ bar bat\ test.sh: Zeile 16: E: Kommando nicht gefunden
- Wenn das nach dem Startoperator
- Tabulator-Einrückung: Damit die Inhaltszeilen und die Ende-Zeichenkette die Einrückungsformatierung des Scripts nicht stören, hat man die Möglichkeit, statt
<<den Operator<<-zu verwenden, wodurch dann Tabulatoren am Zeilenanfang ignoriert werden; sowohl bei den Inhaltszeilen als auch bei der Ende-Zeichenkette:function testf () { if [ "${condition1}" = '1' ]; then if [ "${condition2}" = '1' ]; then cat <<EOT foo ${HOME} bar $( date +%F ) bat $(( 2+5 )) EOT fi fi } function testf () { if [ "${condition1}" = '1' ]; then if [ "${condition2}" = '1' ]; then cat <<-EOT foo ${HOME} bar $( date +%F ) bat $(( 2+5 )) EOT fi fi }
Here-Strings
Here-Strings sind eine kleinere Variante eines Here-Documents. Man kann ein Kommandozeilen-Wort zur Standardeingabe eines Kommandos machen. Zusätzlich zu den Expansions bei Here-Documents steht hier zusätzlich Tilde-Expansion zur Verfügung:
# cat ist unkritisch, zeigt hier nur die Expansionen
$ cat <<<~hl/" foo ${HOME} bar $( date +%F ) bat $(( 2+5 ))"
/home/hl/ foo /home/hl bar 2026-08-20 bat 7
$ read hash rest <<<"$( echo foo | sha1sum )"
declare -p hash
declare -- hash="f1d2d2f924e986ac86fdf7b36c94bcdf32beec15"
Dies ist eine überlegene Alternative zu
$ printf '%s\n' ~hl/" foo ${HOME} bar $( date +%F ) bat $(( 2+5 ))" | cat
# read in einer Pipeline ist kritisch; funktioniert nur mit lastpipe und inaktiver Job-Steuerung
$ echo foo | sha1sum | read hash rest
declare -p hash
bash: declare: hash: Nicht gefunden.
wenn man das Kommando in der normalen Shellumgebung (statt in einer Subshell) ausführen muss, etwa weil man Variablen setzt. Der Vorteil des Here-Strings ist, dass er unabhängig von der Shelloption lastpipe funktioniert.
Umleitung auf TCP oder UDP
Man kann auch auf /dev/tcp/host/port und /dev/udp/host/port umleiten (dies sind keine real existierenden Pfade, sondern spezielle Konstrukte, die Bash erkennt):
exec 4<>/dev/tcp/localhost/80
printf 'GET / HTTP/1.0\r\n\r\n' >&4 ; cat <&4
Pipelines
Mit der Pipe (|) lassen sich Kommandos verketten, indem die Ausgabe des ersten mit der Eingabe des zweiten Kommandos verbunden wird, was sich (fast) beliebig verlängern lässt:
user@blablubb:~/test$ ls -l /etc | grep '^d' | wc -l
Der Mechanismus der Umlenkung der Standardkanäle und die Pipe sind keine Besonderheit der Bash.
Variablen
In der Bash gibt es unterschiedliche Arten von Variablen, die sich in ihrem Namensraum, ihren Attributen und dem Typ ihres Inhalts unterscheiden:
- Namensräume
- innerhalb von Bash
- globale Variablen
- Wenn eine Variable im Hauptprogramm oder ohne
localin einer Funktion definiert wird, steht sie überall im Programm zur Verfügung:var='value'
- Wenn eine Variable im Hauptprogramm oder ohne
- lokale Variablen
- In einer Funktion kann eine Variable mit dem eingebauten Befehl (builtin)
local/declare/typesetdeklariert werden, was sie zu einer Variable macht, die nur in dieser Funktion (und von ihr aufgerufenen Funktionen) sichtbar ist.
- In einer Funktion kann eine Variable mit dem eingebauten Befehl (builtin)
- globale Variablen
- außerhalb von Bash
- Umgebungsvariablen für Bash
- Umgebungsvariablen werden vom Elternprozess festgelegt (zumeist schlicht vererbt). Umgebungsvariablen stehen in der Bash unmittelbar zur Verfügung:
echo "\$HOME: ${HOME}"
- Umgebungsvariablen werden vom Elternprozess festgelegt (zumeist schlicht vererbt). Umgebungsvariablen stehen in der Bash unmittelbar zur Verfügung:
- exportierte Variablen für Kindprozesse
- Wenn die Bash ein externes Kommando ausführt, werden nur die Variablen mit dem Attribut
exportzu Umgebungsvariablen für das externe Kommando. Die Umgebungsvariablen der Bash haben dieses Attribut automatisch gesetzt. Standardmäßig haben Kindprozesse der Bash also dieselben Umgebungsvariablen wie die Bash. - Das Attribut
exportkann bei der Erzeugung einer Variable oder auch später gesetzt (und sogar gelöscht) werden:export var='value'/declare -x var='value'/typeset -x var='value' $ var='value' $ declare -p var declare -- var="value" $ export var $ declare -p var declare -x var="value" $ declare +x var $ declare -p var declare -- var="value"
- Wenn die Bash ein externes Kommando ausführt, werden nur die Variablen mit dem Attribut
- Umgebungsvariablen für Bash
- innerhalb von Bash
- Attribute (Auswahl)
- nur-lesen (
-r) - integer (
-i)- Eine Zeichenkette wird bei Zuweisungen als arithmetischer Ausdruck betrachtet und entsprechend ausgewertet.
$ var=5 $ var+=1 $ declare -p var declare -- var="51" $ var=5 $ declare -i var $ var+=1 $ declare -p var declare -i var="6"
nameref(-n)- Das Attribut
namerefführt eine weitere Möglichkeit der Indirektion ein. Anstatt auf die Variable NAME zuzugreifen, wird auf die Variable zugegriffen, deren Name der Wert von NAME ist. Währendecho "${!varname}"nur lesend funktioniert, kann man mitvarname='foo'die referenzierte Variable überschreiben. $ var='value' $ varname='var' $ echo "${!varname}" value $ varname='newvar' $ echo "${!varname}" $ declare -n varname='var' $ echo "${!varname}" var $ echo "${varname}" value $ varname='foo' $ echo "${varname}" foo $ echo "${var}" foo $ declare -p varname declare -n varname="var"
- Das Attribut
- nur-lesen (
- Typen
- Zeichenketten
- Standardmäßig sind Bash-Variablen Zeichenketten (die im Bedarfsfall automatisch als ganze Zahlen interpretiert werden).
- indizierte Arrays
declare -a iarray=( 'foo' 'bar' 'bat' )erzeugt ein Array, auf dessen Elemente mit numerischen Indizes (natürliche Zahlen inklusive Null) zugegriffen werden kann$ echo "${iarray[1]}" bar $ iarray[5]='baz' $ declare -p iarray declare -a iarray=([0]="foo" [1]="bar" [2]="bat" [5]="baz")
- assoziative Arrays
declare -A aarray=( [foo]='bar' [bat]='baz' )erzeugt ein Array, auf dessen Elemente mit Zeichenketten-Indizes zugegriffen werden kann$ echo "${aarray[foo]}" bar $ aarray[index]='foo bar' $ declare -p aarray declare -A aarray=([index]="foo bar" [foo]="bar" [bat]="baz" )
- Zeichenketten
Kontrollstrukturen
Die Bash kennt folgende Kontrollstrukturen:
- Bedingung / Verzweigung
if- UND-ODER-Listen (
&&,||)
- mustergesteuerte Auswahl
case
- Schleifen
- arithmetische Schleife (
for(( ; ; ))) - Bedingungsschleife (
while,until) - Listenschleife (
for varname in)
- arithmetische Schleife (
Exit-Status / Exit-Code
Diverse Kontrollstrukturen arbeiten mit dem Exit-Status eines Kommandos. Dies ist eine ganze Zahl von 0 bis 255. Normalerweise ist der Bash-Exit-Status der Exit-Code eines externen Kommandos, den der Linux-Kernel an die Bash zurückliefert (bzw. das Äquivalent eines eingebauten Kommandos). Wird das Kommando durch ein Signal beendet, ist der Exit-Status 128 plus die Nummer des Signals (siehe kill -l). Auf den Exit-Status des letzten Kommandos kann man mit $? zugreifen:
$ true ; echo $?
0
$ false ; echo $?
1
Der Exit-Status 0 gilt als wahr, alle anderen als falsch.
Bedingungs-Tests
Für Test, die innerhalb der Bash stattfinden, also nicht durch ein externes Kommando, sondern etwa
- existiert eine Variable (
-v) - ist die Variable (nicht) leer (
-n/-z) - hat die Variable (nicht) einen bestimmten Inhalt (
!=,=) - entspricht der Inhalt einer Variable einem Pattern-Matching-Ausdruck (Globbing oder Regex, nur
[[, siehe dort) - ist der Inhalt einer Variable eine Zahl, die gleich, größer oder kleiner als eine andere Zahl ist
- existiert ein Pfad im Dateisystem (
-a/-e) - ist ein Pfad eine reguläre Datei (
-f), ein Verzeichnis (-d), ein symbolischer Link (-L) usw. - ist eine Datei nicht leer (
-s) - ist eine Datei neuer/älter (mtime) als eine andere (
-nt/-ot) - ist ein Dateideskriptor ein Terminal (
-t) - wird eine Zeichenkette (in dem aktuellen Locale) lexikalisch vor (
<) oder nach (>) einer anderen sortiert
werden v. a. die eingebauten Befehle (builtins) [ / test und das reservierte Wort [[ verwendet. [ ist fast identisch zu test (beide existieren auch als externe Kommandos); der einzige Unterschied ist, dass das letzte Argument ein ] sein muss, damit das Kommando wie ein Klammer-Ausdruck aussieht, den man aus anderen Programmiersprachen kennt (dort allerdings mit runden Klammern):
if [ "$var" = 'yes' ]; then
echo foo
fi
if ! [ -v 'HOME' ]; then
echo "Die Variable '\$HOME' existiert nicht."
fi
if ! [ -d "$HOME" ]; then
echo "Das Verzeichnis '${HOME}' existiert nicht."
fi
if ! [ 'datei_1' -nt 'datei_2' ] && [ -f 'datei_2' ]; then
echo "Die Datei 'datei_1' ist neuer als die Datei 'datei_2'."
fi
if [ "$var" = 'yes' ]; then ist äquivalent zu if test "$var" = 'yes'; then.
[ und test sind für die Bash-Syntax und -Grammatik ganz normale Kommandos. Die einzelnen Elemente müssen also getrennte Wörter sein und (ggf.) mit Quoting geschützt werden: test "$var" = 'foo bar' statt test $var='foo bar'.
| Operator | mathematisches Symbol | Bedeutung |
|---|---|---|
-eq | = | equal |
-ne | != | not equal |
-lt | < | less than |
-le | ≤ | less or equal |
-ge | ≥ | greater or equal |
-gt | > | greater than |
Die beiden Kommandos und die meisten der genannten Operatoren sind in POSIX standardisiert.
Im Gegensatz zu diesen beiden Kommandos ist [[ ein reserviertes Wort der Bash, weswegen zwischen [[ und ]] eine andere Bash-Grammatik gilt als normalerweise:
- Word-Splitting und Pathname-Expansion werden nicht angewendet.
- Diverse Metacharacters und Control-Operators müssen nicht mit Quoting geschützt werden und haben teils eine andere Bedeutung:
<,>,&&,||,(,) - Quoting ändert die Bedeutung von
!von Negation auf literales Zeichen.
Operatoren für [[:
[[ ... ]]versteht alle Operatoren, die[undtestverstehen.[[ ... ]]hat einen zusätzlichen Operator (den[undtestnicht haben):=~für erweiterte reguläre Ausdrücke.- Der Operator
=(oder äquivalent:==) hat eine andere Bedeutung als bei[undtest: Das Wort auf der rechten Seite wird wieextglob-Pattern-Matching interpretiert. - Die Operatoren
&&und||funktionieren innerhalb von[[ ... ]]ähnlich wie ansonsten zwischen Kommandos, aber in diesem Fall verbinden sie Tests.
In beiden Varianten ([[/[) sind mehrere Tests pro Kommando möglich. Allerdings ist dies nur bei [[ eindeutig. Wenn man [ verwendet, sollte man pro Test ein [-Kommando verwenden und diese Kommandos mit && oder || (oder if) verbinden.
| Verknüpfung | [-Operator | [[-Operator |
|---|---|---|
| Negierung | ! | ! |
| UND | -a | && |
| ODER | -o | || |
$ if [ -d "$HOME" ] && [ "$HOME" != "${HOME#/home/}" ]; then echo 'normales Homeverzeichnis'; fi
$ if [[ -d $HOME && $HOME = /home/* ]]; then echo 'normales Homeverzeichnis'; fi
if
if list; then list; [ elif list; then list; ] ... [ else list; ] fi Der Teil zwischen if/elif und then entscheidet darüber, ob ein bzw. welcher Zweig ausgeführt wird. Dies kann ein beliebiges Kommandokonstrukt sein:
- einfaches Kommando
- Pipeline
- eingebautes Kommando (builtin)
- Shellfunktion (
function) - Kommando-Liste (UND, ODER, unbedingte Folge)
- verbundenes Kommando (inklusive eines weiteren
if-Konstrukts)
Die Entscheidung, ob die Bedingung wahr ist, hängt an dem Exit-Status des Kommandos.
Die einfachste Variante ist if true; then echo foo; fi. Mit elif kann man alternative Bedingungen testen:
if list; then
# Falls die Bedingung wahr ist, wird dies ausgeführt.
echo foo
elif list; then
# Falls die erste Bedingung falsch ist, aber die zweite wahr, dann wird dies ausgeführt.
echo bar
fi
Mit else kann man einen Zweig auswählen, falls alle Bedingungen falsch sind.
if list; then
# Falls die Bedingung wahr ist, wird dies ausgeführt.
echo foo
else
# Falls die Bedingung falsch ist, wird dies ausgeführt.
echo bar
fi
Sicherheit
Im September 2014 wurde eine gravierende Sicherheitslücke unter dem Namen Shellshock bekannt. Die seit langem bestehende Lücke ermöglicht, dass beim Start einer neuen Shell Schadcode, der per Umgebungsvariable eingefügt wurde, ungeprüft ausgeführt wird.[8] Die Lücke gilt seit Oktober 2014 als geschlossen.[9][10]
Literatur
- Christian Meißner: Bash – Arbeiten und programmieren mit der Shell. Open Source Press, 2011, ISBN 978-3-941841-44-4.
- Karsten Günther: Bash - kurz & gut. 2008, ISBN 978-3-89721-533-7.
- Cameron Newham, Bill Rosenblatt: Learning the Bash Shell. 3. Aufl., O’Reilly & Associates, 2009, ISBN 0-596-00965-8.
- Jürgen Wolf, Stefan Kania, Frank Sommer: Shell-Programmierung. Das umfassende Handbuch. Rheinwerk Computing, 7. Aufl. 2022, ISBN 978-3-8362-8923-8.
Weblinks
- Homepage
- Bash auf der GNU-Seite
- Advanced Bash-Scripting Guide (englisch)
- Oh My Bash (Konfigurations-Framework)
- Lerne Bash in Y Minuten Übersicht der wichtigsten Elemente und Strukturen
- Bash-Skripting-Guide für Anfänger Einführung bei ubuntuusers.de
- Shell-Programmierung - Einführung, Praxis, Referenz Ausführliches Lern- und Nachschlagwerk, Rheinwerk openbook 2005
Einzelnachweise
- ↑ Chet Ramey: Bash-5.3-release available. 5. Juli 2025 (englisch, abgerufen am 5. Juli 2025).
- ↑ The bash Open Source Project on Open Hub: Languages Page. In: Open Hub. (abgerufen am 3. September 2018).
- ↑ Licensing of Bash. (abgerufen am 3. Oktober 2016).
- ↑ Richard Stallman: The GNU Project. Abgerufen am 23. August 2026.
- ↑ heise.de
- ↑ NEWS
- ↑ Bash-5.0 release available Chet Ramey, 7. Januar 2019, abgerufen am 12. Oktober 2020.
- ↑ ShellShock: Standard-Unix-Shell Bash erlaubt das Ausführen von Schadcode, heise online
- ↑ Michał Zalewski: Bash bug: the other two RCEs, or how we chipped away at the original fix (CVE-2014-6277 and '78). In: lcamtuf blog. 1. Oktober 2014, abgerufen am 31. Oktober 2014 (englisch).
- ↑ Bash Code Injection Vulnerability via Specially Crafted Environment Variables (CVE-2014-6271, CVE-2014-7169). Red Hat, 2. Oktober 2014, abgerufen am 1. November 2014 (englisch).
Auf der Suche nach einem Bash (Shell) Job? Finden Sie hier alle unsere offenen Positionen.
Nicht das richtige Projekt oder den richtigen Job gefunden? Unser Jobboard wird täglich aktualisiert, schauen Sie gerne regelmäßig nach offenen Positionen im Bash (Shell) Umfeld oder anderen IT Technologien.
Bash (Shell) Positionen zeigen
Netzwerke / IT Security
LAN, WAN, Firewall, Penetration Testing, Backup / Recovery, Informationssicherheit, Identity Management, Virenschutz, ServiceNow, Netzwerksicherheit / Cybersecurity, Linux, SUSE Linux, Red Hat / RHEL, Ubuntu, Jitsi, Cloud, Nagios, VPN, VoIP, DNS, Cisco, S/MIME, Unified Communications (UC), IoT, i-doit, Zero Trust Security, Cloudflare, SIEM, NIS-2, Digitale Souveränität, Industrial IT
IT Service & Support
1st Level Support, 2nd Level Support, 3rd Level Support, Helpdesk / Service Desk / UHD, Onsite Support, Field Service, Rollout Techniker, ITIL Experten, Issue-Tracking-System Spezialisten, Service Manager (ITSM), SLA, Asset Management, Monitoring, Solarwinds, Jira, Knowledge Management, Mobile-Device-Management, Chatbot
Projektleitung, Finance & QM
IPMA, PMI, PRINCE2, ITIL, Berater DIN Normen, Berater ISO Normen, BSI Trainer / Auditoren, PMO, IT-Projektleiter / IT-Projektmanager, Transition Manager, Interim Manager, Change Manager, Risk Manager, Lean Manager, Kanban, Business Analysten, Data Scientists, Qualitätssicherung, IFRS, Agiles Management, OKRs, Business Intelligence
Datenbanken
Datenbankadministratoren / DBA, Datenbankentwickler, Datenbankarchitekten, DMS, MS SQL Server, Oracle, PL/SQL, T-SQL, mySQL, NoSQL, DB2, CouchDB, Big Data, MongoDB, MariaDB, DMS, ILIAS, Data Warehouse / DWH, ETL, OLAP, Apache Kafka, Hochverfügbarkeit, UC4, Salesforce, Snowflake, GraphQL, Blockchain, Graphdatenbank, Vektordatenbank
Softwareentwicklung / Emerging Technologies
Front-End, Back-End, Webentwickler, Full-Stack Entwickler, C, C++, C#, .NET, Java, JavaScript, Python, Ruby / Ruby on Rails, HTML, CSS, PHP, TypeScript, DevOps, Agile Softwareentwicklung, Scrum, Docker, React, Spring, Jenkins, Angular, Bash (Shell), iOS, Android, PowerShell, Debugging, Kubernetes, GCP, AWS, OpenShift, ISTQB Tester, SPS, Frameworks, ChatGPT, Flutter, Edge Computing, KI / AI, Quantum Computing, Generative KI, Low-Code-Plattform, Webassembly, LLM
Microsoft Infrastrukturen
Windows Clients, Windows 10, Windows 11, Windows Server 2019, Windows Server 2022, Windows Server 2025, Microsoft 365, Active Directory, Microsoft Azure, GPOs, MS SCCM, MS Office, MS Exchange, BizTalk, MS IIS, Hyper-V, Microsoft Dynamics 365 Business Central, HCL Notes / Domino, VMware, SharePoint, Microsoft Copilot, Microsoft Loop, Microsoft Intune, Microsoft Power Automate
SAP
SAP ERP, SAP Netweaver, Application Server, SAP Business Client, SAP Business Objects Analysis, SAP Cloud Platform, SAP Enterprise Portal, SAP Process Integration, SAP Solution Manager, SAP ITS, ABAP, SAP MaxDB, SAP GUI, SAP FI/CO, SAP APO, SAP SD, SAP PP, SAP MM, SAP BW / SAP BI, SAP HCM, SAP WM, SAP CS, SAP PI, SAP QM, SAP Concur, SAP for Retail, SAP IS-U, SAP IS-A, SAP R/3, SAP HANA, SAP S/4HANA, OpenUI5, Fiori
