Zurück zur Skill-Übersicht

Skill-Wissen und Projektmarkt

Bash (Shell)

Freelancer, Projekte, Experten und Wissen rund um Bash (Shell).

Kategorie

Programmiersprachen

Bash (Shell) Freelancer und Projekte finden

Auf jobtic.com vernetzen sich IT-Freelancer direkt mit Recruitern, Fachbereichsleitern und Projektanbietern. Egal ob du Expertise im Bereich Bash (Shell) suchst oder anbietest, unser intelligenter Algorithmus bringt dich direkt mit den passenden Spezialisten und Projekten zusammen.

Aktuelle Freelancer-Konditionen

Stundensätze 60 - 110 EUR Ø 89 EUR
Tagessätze 550 - 900 EUR Ø 782 EUR

Aktuelle Projekt-Angebote

Derzeit keine verfügbaren Statistiken

Jetzt Top-Freelancer mit fundierter Bash (Shell)-Erfahrung finden

Mehr anzeigen

Auf jobtic.com präsentieren Freelancer Ihr Bash (Shell)-Profil mit CV, Skills, Stundensatz, Projekthistorie und Remote-Präferenzen übersichtlich für Recruiter und Projektanbieter. Auftraggeber finden über die gezielte Freelancer-Suche schnell passende Experten, sowohl für Bash (Shell)-Projekte als auch für andere IT-Bereiche und Skills.

Noch kein Titel gesetzt
Freelancer Profil
Basic

Experte in Bash (Shell)

Noch kein Titel gesetzt

3rd Level Support Bash (Shell) Datenbankadministration
DevOps Engineer
Freelancer Profil
Basic

Fortgeschritten in Bash (Shell)

DevOps Engineer

Administration Linux Puppet

Alles Wissenswerte zum Thema Bash (Shell)

Wikipedia

Bash (Shell)

Artikel öffnen

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

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.

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_SUBSHELL wird inkrementiert.
  • In einer normalen Shell sind $$ und $BASHPID identisch. 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 substituion
    • $ 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.

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, was mit den Kommandos logout oder exit erfolgt, oder per Tastenkombination Strg+D (dargestellt mit ^D, in englischen Anleitungen wie z. B. manpages meist „CTRL-D“ bezeichnet, oder auf Deutsch „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, 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

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 Shelloption globasciiranges (auf C) 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.

Varianten des extended globbing 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

pathname expansion 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 auf stderr lenken:
    $ 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
  • 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.

  • 4 zum Lesen öffnen
    • exec 4<datei_read
    • read line <&4
  • 5 zum Schreiben öffnen
    • exec 5>datei_write
    • echo foo >&5
    • echo bar >&5 # überschreibt die Datei trotz > nicht, weil der Deskriptor nicht neu geöffnet wird
  • 6 zum Anhängen öffnen
    • exec 6>>datei_append
    • echo foo >&6 # kein >> erforderlich, weil der Deskriptor schon für Anhängen geöffnet ist
    • echo bar >&6
  • 7 zum Lesen und Schreiben öffnen
    • exec 7<>datei_read_write
    • read line <&7
    • echo bar >&7
  • beliebigen freien Deskriptor (größer 9) zum Lesen öffnen
    • exec {fdname}<datei_read
    • read 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:

fdinfo-Flags 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äufig EOT fü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
      
  • 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.

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.

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 local in einer Funktion definiert wird, steht sie überall im Programm zur Verfügung: var='value'
      • lokale Variablen
        • In einer Funktion kann eine Variable mit dem eingebauten Befehl (builtin) local / declare / typeset deklariert werden, was sie zu einer Variable macht, die nur in dieser Funktion (und von ihr aufgerufenen Funktionen) sichtbar ist.
    • 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}"
      • exportierte Variablen für Kindprozesse
        • Wenn die Bash ein externes Kommando ausführt, werden nur die Variablen mit dem Attribut export zu 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 export kann 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"
          
  • 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 nameref fü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ährend echo "${!varname}" nur lesend funktioniert, kann man mit varname='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"
        
  • 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" )
        

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, Frank Sommer: Shell-Programmierung. Das umfassende Handbuch. Rheinwerk Computing, 7. Aufl. 2022, ISBN 978-3-8362-8923-8.

Weblinks

Einzelnachweise

  1. Chet Ramey: Bash-5.3-release available. 5. Juli 2025 (englisch, abgerufen am 5. Juli 2025).
  2. The bash Open Source Project on Open Hub: Languages Page. In: Open Hub. (abgerufen am 3. September 2018).
  3. Licensing of Bash. (abgerufen am 3. Oktober 2016).
  4. Richard Stallman: The GNU Project. Archiviert vom Original (nicht mehr online verfügbar) am 3. September 2024; abgerufen am 14. Februar 2024.  Info: Der Archivlink wurde automatisch eingesetzt und noch nicht geprüft. Bitte prüfe Original- und Archivlink gemäß Anleitung und entferne dann diesen Hinweis.@1@2Vorlage:Webachiv/IABot/www.gnu.org
  5. heise.de
  6. NEWS
  7. Bash-5.0 release available Chet Ramey, 7. Januar 2019, abgerufen am 12. Oktober 2020.
  8. ShellShock: Standard-Unix-Shell Bash erlaubt das Ausführen von Schadcode, heise online
  9. 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).
  10. 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).
Wikipedia

Dieser Text basiert auf dem Artikel Bash (Shell) aus der freien Enzyklopädie Wikipedia und steht unter der Lizenz Creative Commons CC-BY-SA 3.0 Unported. Eine Liste der Autoren ist in der Wikipedia verfügbar.

Programmiersprachen

Kontakte aufbauen

Bau mit jobtic.com dein Netzwerk im Bereich Bash (Shell) und anderen Fachgebieten gezielt auf. Die Plattform bringt dich ohne Umwege mit den richtigen Geschäftskontakten zusammen, sowohl für die Projektakquise und Expertensuche als auch den Austausch mit der Freelancer-Community.

Vernetzung stärken

Auf jobtic verbindest du dich direkt mit spezialisierten Freelancern und Auftraggebern rund um Bash (Shell) und andere IT-Themen. Die Plattform vereint dabei Wissensdatenbank und Projektmarkt. So knüpfst du wertvolle Kontakte und findest sofort die passenden Köpfe oder IT-Projekte.

Chancen entdecken

Präsentiere deine Erfahrung auf jobtic und erhalte direkten Zugriff auf aktuelle IT-Projekte. Dank moderner Filter findest du schnell die passenden Auftraggeber, Recruiter und neuen Aufträge für dein Business.

Passende Projekte zu Bash (Shell)

Mehr Projekte