Bash (Shell)
Bash (auch BASH oder bash), die Bourne-again shell, ist eine freie Unix-Shell unter GPL.
Bash — The Bourne-again shell | |
---|---|
Beispiel einer bash-Sitzung | |
Basisdaten | |
Maintainer | Chet Ramey |
Entwickler | Chet Ramey |
Erscheinungsjahr | 8. Juni 1989 |
Aktuelle Version | 5.1.16[1] (5. Januar 2022) |
Betriebssystem | verschiedene |
Programmiersprache | C[2] |
Kategorie | Kommandozeileninterpreter |
Lizenz | GNU General Public License, Version 3.0 oder später[3] |
deutschsprachig | ja |
www.gnu.org/software/bash/ |
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.
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
Die Bash wurde 1987 von Brian Fox für das GNU-Projekt geschrieben 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
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. der 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 (Prompt), sitzungsübergreifend zu ermöglichen.
Subshell
Eine Subshell ist ein Shellprozess, der von einer Shell erzeugt wurde. Programme, die durch Eingabe eines Kommandos in eine Shell zur Ausführung gebracht werden, werden betriebssystembedingt in einer Subshell gestartet: Bevor die Shell ein Programm als Kindprozess starten kann, muss sie einen Shellprozess erzeugen. Bei der Ausführung eines Programms aus der grafischen Benutzeroberfläche heraus ist jedoch keine Shell oder Subshell involviert.
Subshells werden automatisch bei der Verwendung einer Reihe von Bashfeatures erzeugt. Beispielsweise werden alle Befehle einer Pipeline in einer eigenen Subshell ausgeführt. Shells in grafischen Oberflächen, wie kterm
oder gnome-terminal
, sind keine Subshells, da ihr Elternprozess keine Shell ist.
Login-Shell
Wird die Bash mit einem Programm gestartet, das der Anmeldung an der Konsole dient, wie z. B. login
, su -
, ssh
o. ä., oder mit der Option -l
aufgerufen, fungiert sie als interaktive Login-Shell. Die Verwendung der Bash als Login-Shell für einen Benutzer wird durch den entsprechenden Benutzereintrag in der /etc/passwd
festgelegt. Das Verhalten einer Login-Shell weicht von dem einer Nicht-Login-Shell ab. Wie jede Shell liest die Bash (zunächst) die systemweite Konfigurationsdatei /etc/profile
und führt die darin enthaltenen Anweisungen aus, danach sucht sie im Benutzerverzeichnis des angemeldeten Benutzers der Reihe nach ~/.bash_profile
, ~/.bash_login
, ~/.profile
, wobei nur die erste gefundene Datei gelesen wird. Mit dem Beenden der Login-Shell wird der angemeldete Benutzer abgemeldet (logout
oder exit
) oder ^D
CTRL-D (oder auf der deutschen Tastatur STRG-D). Bei der Abmeldung wird — sofern vorhanden — die ~/.bash_logout
ausgeführt.
Konfiguration
Die Bash wird durch eine Reihe von Konfigurationsdateien konfiguriert. Hierbei ist zu unterscheiden zwischen systemweiter und benutzerspezifischer Konfiguration.
Die systemweiten Einstellungen der Bash werden — je nach Distribution — in der /etc/bash.bashrc
oder der /etc/bashrc
gespeichert. Die systemweite /etc/profile
wird schon durch die Login-Shell ausgewertet.
Das Shell-Profil wird jedes Mal ausgeführt, wenn eine Shell-Instanz startet, also auch (aber nicht nur) dann, wenn eine Login-Shell gestartet wird.
Im Verzeichnis /etc/skel
werden Schablonen der Konfigurationsdateien bereitgestellt, die in das Heimatverzeichnis eines neu erstellten Benutzers kopiert werden.
Eingebaute Befehle
Die Bash enthält zahlreiche eingebaute Befehle und reservierte Wörter. Zu den eingebauten Befehlen zählen beispielsweise alias
, cd
, 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
.
Platzhalter
Als Platzhalter verwendet die Bash den *
für beliebig viele Zeichen (also auch keines) und das ?
für genau ein Zeichen. Mit [
]
lassen sich auch Zeichenmengen und mit {
}
sogenannte Klammerexpansion angeben.
Beispiele:
user1@blablubb:~/test$ ls datei*
datei datei1 datei2 datei3 datei4 datei5 datei6 # Auch 'datei' ohne Nummer
user1@blablubb:~/test$ ls datei?
datei1 datei2 datei3 datei4 datei5 datei6
user1@blablubb:~/test$ ls datei[1-3] # Wertebereich
datei1 datei2 datei3
user1@blablubb:~/test$ ls datei[135] # Wertemenge
datei1 datei3 datei5
user1@blablubb:~/test$ touch test{1..5} # Wertebereich für Klammernexpansion
user1@blablubb:~/test$ ls test*
test1 test2 test3 test4 test5
user1@blablubb:~/test$
Ein- und Ausgabe, Umleitung
Wie in Unix üblich, unterstützt die Bash die Umlenkung der drei Standard-Datenströme (auch Standardkanäle), die mittels der sogenannten Dateideskriptoren gehandhabt werden. Es sind dies die Standardeingabe stdin
(Kanal 0), die Standardausgabe stdout
(Kanal 1) und die Standardfehlerausgabe stderr
(Kanal 2). Ein in der Bash ausgeführtes Programm liest von der Standardeingabe, üblicherweise die Tastatur, und gibt das Ergebnis an die Standardausgabe weiter, üblicherweise der Bildschirm. Gewöhnlich werden Fehlermeldungen über die Standardfehlerausgabe stderr
auf der Standardausgabe stdout
, also am Bildschirm ausgegeben, wie das folgende Beispiel verdeutlicht:
user@blablubb:~/test$ ls -l /root ~/test
/home/user/test:
insgesamt 0
-rw-r--r-- 1 user1 users 0 Feb 11 20:03 datei1
-rw-r--r-- 1 user1 users 0 Feb 11 20:03 datei2
ls: Öffnen von Verzeichnis /root nicht möglich: Keine Berechtigung
Die Kanäle können auch kürzer über ihre Nummern angesprochen werden.
Die Standardkanäle können auch umgeleitet werden, indem hinter dem jeweiligen Kommando die Umlenkzeichen <
(Kanal 0), >
(Kanal 1) und |
(Verkettung von Ein- und Ausgabe) verwendet werden.
In obigem Beispiel wurde die Fehlermeldung zusammen mit der Standardausgabe auf dem Bildschirm ausgegeben. Will man Fehler- und Standardausgabe trennen und in verschiedenen Dateien speichern, kann man das mittels der Kanalnummern tun:
user@blablubb:~/test$ ls -l /root ~/test >ls.txt 2>ls_err.txt
Nachfolgend wird die Standardausgabe von ls *.txt
in die Datei verzeichnis.info
umgeleitet und danach von less
als Standardeingabe eingelesen:
user@blablubb:~/test$ ls *.txt > verzeichnis.info
user@blablubb:~/test$ less < verzeichnis.info
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.
set-Optionen
Die Bash verfügt über 27 Optionen, mit denen ein anderer Betriebsmodus eingestellt werden kann. Alle möglichen Einstellungen können mit
user$ set -o
aufgelistet werden, wobei mit der Option -o
die Modi gelistet werden bzw. gesetzt werden und mit der Option +o
die Option wieder aufgehoben wird. Das +
ist dabei als durchgestrichenes -
zu lesen. Gebräuchlich sind die Optionen noclobber
, mit der der Ausgabeumleitung untersagt wird, vorhandene Dateien zu überschreiben, die Option noglob
, bei der für Dateinamen keine Platzhalter wie *
und ?
möglich sind und die Option xtrace
, bei der jedes Shell-Kommando vor der Ausführung nochmals ausgegeben wird, und zwar mit den intern vorgenommenen Erweiterungen, was bei der Fehlersuche nützlich sein kann.
Das Überschreiben der Ausgabeumleitung wird unterdrückt:
user$ echo "hallo" > hallo.txt
user$ echo "hallo" > hallo.txt
user$ set -o noclobber
user$ echo "hallo" > hallo.txt
-su: hallo.txt: Kann existierende Datei nicht überschreiben.
user$ set +o noclobber
user$ echo "hallo" > hallo.txt
Man kann Dateinamen mit Stern und Fragezeichen erzeugen. Vor der Abfrage mit ls *sh
muss die Option wieder zurückgesetzt werden, denn sonst würde nach der Datei *sh
gesucht werden.
user$ set -o noglob
user$ touch da*tei.sh
user$ touch dat?ei.sh
user$ set +o noglob
user$ ls *sh
da*tei.sh dat?ei.sh
Die dritte Zeile zeigt die von der Bash vorgenommenen Erweiterungen an:
user$ set -o xtrace
user$ ls *.txt
+ ls --color=auto hallo.txt test.txt text.txt
hallo.txt test.txt text.txt
Alle Optionen können auch mittels einer Kurzschreibweise gesetzt werden, beispielsweise ist set -C
gleichbedeutend mit set -o noclobber
.
Umgebungsvariablen
In der Bash können Variablen auf mehrere Arten definiert werden. Grundsätzlich unterscheidet man zwischen „lokalen“ Variablen, die nur in der Shell gelten, in der sie definiert wurden, und „globalen“ Variablen, die auch in Sub-Prozessen zugewiesen sind. Lokale Variablen können erzeugt werden mit:
user$ var=var1
user$ var='var zwei'
user$ let var=var3
user$ declare var=var4
und globale Variablen werden definiert mit:
user$ declare -x var=var5
user$ export var=var6
Angesprochen werden die Variablen mit dem $
-Zeichen:
user$ echo $var
Statt „globale Variable“ ist allerdings im Unix-Umfeld der Ausdruck „Umgebungsvariable“ sehr viel geläufiger und passender, weil in manchen Programmiersprachen der Ausdruck „globale Variable“ eine andere Bedeutung als „Umgebungsvariable“ hat. Eine Wertzuweisung für eine „Umgebungsvariable“ wirkt nur auf „Unterprogramme“ (Kindprozesse), während manche Programmiersprachen es erlauben, Variablenwerte auch für „Oberprogramme“ zu setzen (etwa durch Voranstellen von \global
in TeX).
Eigene Umgebungsvariablen
Die Bash verfügt über zahlreiche eigene Umgebungsvariablen, die auch häufig von anderen Kommandos ausgewertet werden. Bekannt sind die Variablen PATH
für den Suchpfad, LANG
für die Einstellung der Sprache oder PS1
für den Prompt.
Beispielsweise können die englischsprachigen Hilfeseiten vom ls
mit dem nachfolgenden Befehl aufgerufen werden. Hier wird eine Sub-Shell erzeugt, darin die LANG
-Umgebungsvariable überschrieben und anschließend der Befehl man ls
ausgeführt.
user$ LANG=en man ls
Programmierung
Bash-Programmierung unterscheidet sich in vielen Punkten von anderen Programmiersprachen. So wird beispielsweise bei der Verzweigung traditionell die Bedingung nicht von der Shell selbst ausgewertet, sondern an ein weiteres Programm übergeben:
if [ Bedingung ] ; then
# Falls Bedingung wahr ist, wird dies ausgeführt
else
# Falls Bedingung falsch ist, wird dies ausgeführt
fi
Die beiden eckigen Klammern sind keine Begrenzer, sondern ein Synonym für den integrierten (builtin) Shell-Befehl test
. Der Befehl test
prüft die Bedingung und liefert einen Rückgabewert 0 (wahr) oder 1 (falsch), der von der if
-Anweisung verarbeitet wird. Der oben angeführte Code ist also identisch mit der folgenden Schreibweise:
if test Bedingung ; then
# Falls Bedingung wahr ist, wird dies ausgeführt
else
# Falls Bedingung falsch ist, wird dies ausgeführt
fi
Heutzutage existiert jedoch auch ein eingebauter Ausdruck, der ohne externen Befehl auskommt, daher nicht mehr den Beschränkungen unterliegt, denen die Befehlsform unterlag, und so unter anderem auch keine Anführungszeichen um Variablen mehr benötigt:
if [[ Bedingung ]] ; then
# Falls Bedingung wahr ist, wird dies ausgeführt
else
# Falls Bedingung falsch ist, wird dies ausgeführt
fi
Hier kann die Bedingung nun z. B. auch Operatoren wie <
, >
(kleiner als und größer als), und =~
(Vergleich mit Regulärem Ausdruck) enthalten.
Diese Variante wird daher, wenn keine Kompatibilität zu älteren Versionen benötigt wird, generell empfohlen.
Jedoch kann auch weiterhin der Rückgabewert (0–127, wobei >0 = falsch) eines beliebigen Programmes verarbeitet werden. Nicht nur der des test
-Befehls. Als Beispiel wird hier der kill
-Befehl verwendet, um zu testen, ob ein Prozess mit einer bestimmten Nummer noch läuft bzw. in der Lage ist, Signale entgegenzunehmen:
if kill -0 1234 ; then
# Prozess 1234 läuft
else
# Prozess 1234 läuft nicht
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: Shell-Programmierung. Das umfassende Handbuch. Galileo Computing, 4. Aufl. 2013, ISBN 978-3-8362-2310-2.
Weblinks
Einzelnachweise
- Bash-5.1 Official Patch 16. (abgerufen am 4. Februar 2022).
- 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).
- About the GNU Project - GNU Project - Free Software Foundation (FSF). 24. April 2011, abgerufen am 11. Mai 2019.
- 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).