Aus Linux-Magazin 09/2025

Bash 5.3 bringt Neuerungen zum Beschleunigen und Entschlacken von Skripten

© elena moiseeva / 123RF.com

Die Bourne Again Shell, kurz Bash, erscheint in Version 5.3 und bringt interessante Neuerungen. Vor allem bei Skripten dürfte die Performance steigen. Ich habe mir den Release Candidate 2 und vor allem die dazugekommenen Features genauer angesehen.

Die Bash gehört bis heute zu den am weitesten verbreiteten Befehlsinterpreter überhaupt. Wenngleich zahlreiche Distributionen längst die Dash zur Standard-Shell ernannt haben, zweifelt sicher kaum jemand ernsthaft an der Daseinsberechtigung des Platzhirschen.

Wie nahezu jede Linux-Software liegt auch Bash zunächst im Quellcode vor. Wenn Sie sie installieren möchten, müssen Sie dementsprechend einige Vorkehrungen treffen. Im ersten Schritt benötigen Sie das Paket »build-essential«. Es verfügt über Abhängigkeiten zu wichtigen Paketen wie »libc6-dev«, »gcc«, »g++« und »make«.

Sobald ich das Kommando »sudo apt install build-essential« aufgerufen habe, ist mein System bereit, aus dem Quellcode die Binaries zu erzeugen. Weil Bash bereits durch meine Distro auf dem System eingebunden ist, möchte ich meine produktive Umgebung nicht mit distributionsfremder Software stören.

Aus diesem Grund installiere ich die Bash 5.3 nicht – wie prinzipiell als Standardweg vorgesehen – nach »/usr/local/«, sondern in einen eigens angelegten Ordner namens »/opt/bash/bash-5.3rc2«. Er findet sich nicht in der »$PATH«-Variablen und funkt somit im Betrieb meiner produktiven Umgebung nicht dazwischen.

Installieren

Per tar ist das Paket in Windeseile entpackt. Danach steht die Konfiguration auf dem Programm. Wie häufig existiert – sofern die Maintainer der jeweiligen Software auf »autoconf« setzen – dazu ein Konfigurationsskript. Mithilfe dessen lassen sich die Kompilierung und Installation anpassen.

Für meinen konkreten Fall spielt jetzt nur der Parameter »–prefix« eine Rolle (Abbildung 1). Er steuert, in welche Ordnerstruktur das Paket installiert werden soll. Für diesen Artikel ist das wie erwähnt »/opt/bash/bash-5.3rc2«. Im Anschluss an »configure« braucht es noch die beiden Aufrufe »make« und »sudo make install« (Abbildung 2).

Abbildung 1: Nach dem Entpacken konfigurieren Sie die Software per »configure«.

Abbildung 1: Nach dem Entpacken konfigurieren Sie die Software per »configure«.

Abbildung 2: Während »make« sich um die Kompilierung kümmert, übernimmt »make install« die Installation der Dateien.

Abbildung 2: Während »make« sich um die Kompilierung kümmert, übernimmt »make install« die Installation der Dateien.

Wer Bash selbst kompiliert, sollte beachten, dass es in der jüngsten Version größere Änderungen an der Codebasis gab. Mit der vermutlich bald erscheinenden Version 5.3 [1] vollzieht Bash die Umstellung auf den C23-Standard, genauer den Standard ISO/IEC 9899:2024 (en) [1]. Damit wird sich die Shell nicht mehr länger mit Compilern, die den ursprünglichen Kernighan-und-Ritchie-Stil (K&R) verarbeiten, kompilieren lassen.

Neue Checks

Mit Version 5.3 prüft Bash anhand der ersten beiden Zeilen einer Datei, ob es sich dabei wirklich um ein Skript oder doch um eine Binärdatei handelt. Bekanntermaßen beginnen nicht nur Shell-Skripte, sondern auch die Skripte der Interpretersprachen wie Python, Perl oder Ruby mit einem Shebang »#!«. An ihn schließt sich üblicherweise der Pfad zum Interpreter an, zum Beispiel »/bin/bash«, »/usr/bin/env perl« und so weiter.

Eine weitere, erfreuliche Neuerung: Bei fehlendem »fi« gibt Bash neuerdings die Zeilennummer des zuletzt geöffneten »if« aus. Bislang lieferte die Shell stets die wenig hilfreiche Fehlermeldung: Zeile X: Syntaxfehler: Unerwartetes Dateiende..

Wenn ich nun das fehlerhafte Skript aus Listing 1 ausführe, erhalte ich aufgrund des fehlenden, abschließenden »fi« die erheblich hilfreichere Meldung: “Zeile 8: Syntaxfehler: Unerwartetes Dateiende vom Kommando ‘if’ in Zeile 2.”. Dasselbe funktioniert ebenfalls für »while«- und »for«-Loops, die nicht durch »done« terminiert werden.

Listing 1

Fehlendes fi

#!/opt/bash/bash-5.3/bin/bash -
if [ "$1" = "a" ]; then
  echo "A"
else
  echo "Nicht A"

Auch Freunde regulärer Ausdrücke kommen bei der neuen Release auf ihre Kosten. Während Bash 5.2 das folgende unsinnige Pattern klaglos annimmt (der Quantor »b« müsste eine Ziffer sein)

[[ "$1" =~ [0-9]{b} ]] && echo "$1" || echo "Nope"

und schlicht Nope ausgibt, liefert Bash 5.3 eine ausgesprochen gute Fehlermeldung: “Zeile 2: [[: ungültiger regulärer Ausdruck `[0-9]{b}i’: Ungültiger Inhalt von »\{\}«”.

Neue Umgebungen

Mit GLOBSORT führt Version 5.3 darüber hinaus eine neue Umgebungsvariable ein, mit der Sie die Sortierung der Ergebnisse von Pfadnamenvervollständigung steuern können. Als Sortierfelder kommen name, size, blocks, mtime, atime, ctime, numeric, oder none infrage. Zudem kann die Sortierrichtung auf- und absteigend sein.

Das Kommando »source« lässt sich jetzt mit dem Schalter »-p PATH« explizit auf einen bestimmten Pfad einschränken. Damit sind Entwickler und Admins künftig in der Lage, abseits von »$PATH« eigene Umgebungen aufzubauen. Innerhalb derer könne sie auch den Quellcode mit einbeziehen. Mein Bauchgefühl sagt mir, dass das noch ein sehr spannendes Thema werden könnte.

Command Substitutions

Alte Hasen verwenden nach wie vor gern Konstrukte wie »lines=`wc -l /etc/fstab`«, obwohl die Backtick-Notation, gerade bei Verwendung von Backslashes »\«, als fehlerbehaftet gilt. An dieser Stelle sollte besser die Dollar-Notation »lines=$(wc -l /etc/fstab)« zum Einsatz kommen.

Allerdings bringen beide einen entscheidenden Nachteil mit. Sie forken eine Subshell, also einen neuen Prozess. Das bedeutet nicht nur einen erheblichen Overhead, falls viele Subshells laufen. Es kann auch Verwirrung stiften, etwa bei Variablen, die innerhalb der Subshell erzeugt werden.

Hierzu gibt es gleich zwei neue Syntaxelemente: »${ cmd1; … ; cmdN; }« und »${| cmd1; … ; cmdN; }«. Ersteres ist das neue Pendant zu »$( cmd1; … cmdN; )«, mit dem Unterschied, dass die enthaltenen Kommandos ohne Fork in derselben Shell ausgeführt werden.

Subshells, wie sie in Listing 2 in der dritten Zeile zum Einsatz kommen, führen dazu, dass das Skript »File has 24 lines« ausgibt. Die Variable »myfile« ist außerhalb der Subshell leer. Listing 3 verwendet in der dritten Zeile die neue Syntax, die dafür sorgt, dass »myfile« im demselben Kontext wie das Skript ausgeführt wird. Damit passt auch die Ausgabe »File /etc/fstab has 24 lines.«.

Listing 2

Command Substitution mit Subshell

#!/bin/bash
myfolder="/etc"
lines=$( myfile="${myfolder}/fstab"; wc -l "$myfile" | cut -d' ' -f 1; )
echo "File ${myfile} has ${lines} lines"

Listing 3

${ cmd1; … ; cmdN; }

#!/opt/bash/bash-5.3/bin/bash
myfolder="/etc"
lines=${ myfile="${myfolder}/fstab"; wc -l "$myfile" | cut -d' ' -f 1; }
echo "File ${myfile} has ${lines} lines."

Das zweite neue Syntaxelement »${| cmd1; …; cmdN; }« fällt weniger intuitiv aus, behandelt aber ähnliche Use-Cases. Es ist vor allem sinnvoll, wenn Sie die volle Kontrolle über das Ergebnis der Substitution haben möchten, Performance hinzugewinnen wollen, denn Forks werden ja vermieden, und wenn Sie Side-Effects im aktuellen Shell-Kontext beibehalten möchten (Listing 4).

Listing 4

${| cmd1; …; cmdN; }

§§number
#!/opt/bash/bash-5.3/bin/bash
REPLY="Bash-Shell"
myfile="/etc/fstab"
thefile=${| wc -l "$myfile" | cut -d' ' -f 1; date +%Y-%m-%d ; REPLY=${myfile}; }
echo "File: ${thefile}; REPLY: ${REPLY}"

In der zweiten Zeile wird die Variable REPLY gesetzt. Das braucht es normalerweise nicht, da »REPLY« automatisch, beispielsweise durch »read« gesetzt wird, wenn man »read« kein Variablenargument zuordnet. Somit ist »read« flüchtig.

In der dritten Zeile definiere ich eine Datei, auf die ich in der vierten Zeile entsprechende Operationen anwende. Zunächst gibt »wc« die Anzahl an Zeilen aus, daraufhin wird das aktuelle Datum nach »stdout« geschrieben. Abschließend weise ich »REPLY« den Dateinamen zu.

Aufmerksame Leser haben es bereits erkannt: »${| …; }« fängt die Standardausgabe nicht ab. Als Ergebnis des Konstrukts wird letztlich »REPLY« an die Zuweisung geliefert. Das heißt, »thefile« enthält nach dieser Zeile »/etc/fstab«. Mit dem Abschluss der Command Substitution erhält obendrein »REPLY« den ursprünglichen Wert »Bash-Shell« zurück. Der eigentliche Nutzen des Konstrukts liegt in der Kontexterhaltung, auch wenn sich das nicht auf den ersten Blick (Abbildung 3) erschließt.

Fazit

Bash 5.3 bringt einen ganzen Strauß wichtiger Neuerungen mit, die sich jedoch hauptsächlich an Entwickler und Power-User wenden. Der Umstieg auf den C23-Standard dürfte aus Entwicklersicht wohl der wichtigste Schritt sein. Weitere neue Features, die ich im Rahmen dieser Kolumne nicht vollumfänglich abdecken kann, hängen mit dem Thema Auto-Completion zusammen.

Die vollständigen Release-Notes [2] lesen Sie online nach. Bezüglich der Auto-Completion dürfen wir wohl zahlreiche interessante neue Abkömmlinge erwarten. Für mich ist die neue Command-Substitution momentan das interessanteste Feature. Damit kann ich vermutlich einige Skripte erheblich beschleunigen und andere abspecken. Der Einzige Wermutstropfen für mich: Ich muss wohl noch einiges an Geduld aufbringen, bis die Release auch auf Enterprise-Distributionen wie SLES oder RHEL ankommt. (csi)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 3 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben