Nushell – die Shell der nächsten Generation
Seit Anbeginn der Zeit sind Linux/Unix-Shells größtenteils POSIX-kompatibel. Obwohl sie weit verbreitet sind, teilen POSIX-kompatible Shells dieselben Einschränkungen und schlechten Designentscheidungen. POSIX-Shell-Skripte können bei falscher Ausführung zu unerwarteten Fehlern und Systemausfällen führen.
Dieser Artikel wurde ursprünglich auf Englisch verfasst. Sie lesen hier die übersetzte Version. Das englische Original finden Sie hier: Originalartikel lesen
In computerwissenschaftlichen Bereichen bremsen Shell-Skripte jeden Umsetzungs-Prozess aus, der auf Betriebssystem-Level Automatisierung angewiesen ist. Das gilt besonders für Ops- und DevOps-Anwendungen wie CI/CD (Continuous Integration mit Continuous Delivery/Deployment), bei denen es nicht praktikabel ist, eine Laufzeitumgebung oder Toolchain für eine typsichere / nullsichere Hochsprache bereitzustellen. Gerade in Ops und DevOps brauchen wir gutes Werkzeug, um einen sicheren und fehlertoleranten Betrieb zu gewährleisten.
Wir bei RiKuWe versuchen, die Angriffsfläche potenzieller Fehler so gering wie möglich zu halten – darum sind wir immer auf der Suche nach besseren und zuverlässigeren Technologien, um die Qualität unserer Services zu verbessern – für uns selbst und für unsere Kund*innen. In diesem Artikel stelle ich eine Shell namens Nushell vor, die eine großartige Alternative zu POSIX-kompatiblen Shells für Ops- und DevOps-Umgebungen bietet.
Was ist Nushell?
Nushell ist eine Shell, die in Rust geschrieben wurde. Auch wenn sich die Nu-Skriptsprache stark an Rust anlehnt, enthält sie viele Funktionen, die man aus anderen Shells kennt – etwa das Zugreifen auf Umgebungsvariablen wie in PowerShell oder Unix-ähnliche Pipes zur Datenverarbeitung. Im Gegensatz zu anderen Shells ist Nushell jedoch typ- und objektbewusst. Die Nu-Skriptsprache erlaubt Typannotationen für Befehle (Funktionen) und Variablen. Wird ein Wert einem falschen Typ zugewiesen, meldet Nushell einen Fehler. Anders als bei Rust erfolgt die Typüberprüfung zur Laufzeit, aber der eingebaute Language Server meldet Typverletzungen bereits beim Schreiben.
Nushell-Typen
- records: Objektähnliche Datentypen
- tables: Tabellen mit Zeilen, die alle dieselbe Struktur und Typen besitzen
- lists: Beliebige Anzahl an Elementen, erweiterbar und modifizierbar
- directory: Ein gültiger Pfad zu einem Verzeichnis
- path: Ein gültiger Pfad zu einer Datei
- strings: Normale Zeichenketten, Ausgaben nushell fremder Befehle/Applikationen werden als Strings interpretiert
- und viele mehr...
Nushell-Beispiel
Einfaches Pipeline-Beispiel:
Dieses Beispiel nutzt ausschließlich eingebaute Nushell-Funktionen.
Der Befehl ls
in Nushell gibt eine strukturierte Tabelle mit den Verzeichnisinhalten zurück.
Diese Tabelle können wir filtern und verändern.
ls erzeugt folgende Tabelle
╭────┬───────┬─────────┬────────┬───────────────╮
│ # │ name │ type │ size │ modified │
├────┼───────┼─────────┼────────┼───────────────┤
│ 0 │ ISO │ dir │ 62 B │ 8 months ago │
│ 1 │ bin │ symlink │ 7 B │ 4 months ago │
│ 2 │ boot │ dir │ 4.0 kB │ 55 years ago │
│ 3 │ cache │ dir │ 22 B │ 3 months ago │
│ 4 │ dev │ dir │ 4.1 kB │ 21 hours ago │
│ 5 │ etc │ dir │ 4.1 kB │ 2 hours ago │
│ 6 │ home │ dir │ 10 B │ 9 months ago │
│ 7 │ lib │ symlink │ 7 B │ 4 months ago │
│ 8 │ lib64 │ symlink │ 7 B │ 4 months ago │
│ 9 │ mnt │ dir │ 38 B │ 2 months ago │
│ 10 │ opt │ dir │ 154 B │ 2 months ago │
│ 11 │ proc │ dir │ 0 B │ a day ago │
│ 12 │ root │ dir │ 206 B │ 3 weeks ago │
│ 13 │ run │ dir │ 880 B │ 21 hours ago │
│ 14 │ sbin │ symlink │ 7 B │ 4 months ago │
│ 15 │ srv │ dir │ 14 B │ 9 months ago │
│ 16 │ sys │ dir │ 0 B │ a day ago │
│ 17 │ tmp │ dir │ 940 B │ 2 minutes ago │
│ 18 │ usr │ dir │ 112 B │ 2 hours ago │
│ 19 │ var │ dir │ 126 B │ a day ago │
│ 20 │ vms │ dir │ 52 B │ 2 weeks ago │
╰────┴───────┴─────────┴────────┴───────────────╯
Die Nu-Pipeline
Solche Befehle, bei denen Ergebnisse durch Pipes verarbeitet werden, nennt man Nu-Pipelines.
Diese Nu-Pipeline listet den Inhalt meines Root-Verzeichnisses (Ich nutze übrigens Arch Linux 😉), entfernt alle Verzeichnisse und Einträge,
die kleiner als 1KB
sind, sortiert nach Größe, wählt nur die Spalten name, type und size und wandelt das Ergebnis in eine Markdown-Tabelle um.
Da dieser Artikel in Markdown geschrieben ist, wird die Tabelle auch korrekt gerendert.
Beachte 1KB
im Beispiel: Da die Spalte size vom Typ filesize ist, muss der Vergleichswert ebenfalls ein gültiger Dateigrößenwert sein.
ls / | where type == dir and size > 1KB | sort-by size | select name type size | to md --pretty
Ergebnis: Markdown-Tabelle
name | type | size |
---|---|---|
/boot | dir | 4.0 kB |
/dev | dir | 4.1 kB |
/etc | dir | 4.1 kB |
Scripting-Vergleich: Nushell vs Bash
In den folgenden Beispielen nutze ich den Befehl cat
, da dessen Fehlermeldungen die übergebenen Argumente gut abbilden.
Zudem zeigt das Beispiel, dass Nushell problemlos mit systemeigenen Binärdateien funktioniert.
Bash
export SOME_ENV_VAR="some string with spaces"
# this results in 4 arguments instead of one
echo $SOME_ENV_VAR
# when used with cat: sh-5.2$ cat $SOME_ENV_VAR
output:
cat: some: No such file or directory
cat: string: No such file or directory
cat: with: No such file or directory
cat: spaces: No such file or directory
Die Lösung in Bash ist, das Argument in ""
zu setzen, also "${SOME_ENV_VAR}"
.
Das funktioniert auch, wenn die Umgebungsvariable nicht gesetzt ist – in dem Fall wird der Fehler jedoch verschleiert, da eine leere Zeichenkette übergeben wird.
Die zwei Optionen in Bash:
- merkwürdiges Verhalten, weil die Shell Werte als einzelne Argumente interpretiert
- merkwürdiges Verhalten, weil die Shell nicht gesetzte Variablen ignoriert
- versteckte Fehler durch leere Zeichenketten bei
""
Erfahrene Bash-User setzen am Anfang set -eu
, aber nicht jede Shell unterstützt das,
und im DevOps-Umfeld haben wir oft mit unterschiedlichen Shells zu tun – vor allem wegen der vielen verschiedenen Docker-Container, die beim Bauen und Deployen zum Einsatz kommen.
Nushell
Nushell versteht Variablen, Argumente und deren Typen. Anstatt jedes Wort als Argument zu interpretieren, erkennt Nushell, dass nur ein einziges Argument übergeben wird.
$env.SOME_ENV_VAR_WITH_SPACES = "some string with spaces"
cat $env.SOME_ENV_VAR_WITH_SPACES
output:
cat: 'some string with spaces': No such file or directory
Nushell erlaubt es auch, eine Variable in mehrere Argumente aufzuspalten – mithilfe des Spread-Operators ...
$env.MULTIPLE_ARGUMENTS = ["first", "second", "third"]
cat ...($env.MULTIPLE_ARGUMENTS)
output:
cat: first: No such file or directory
cat: second: No such file or directory
cat: third: No such file or directory
Wird eine nicht gesetzte Variable verwendet, gibt Nushell einen klaren Fehler zurück:
cat $env.UNSET_VARIABLE
output:
Error: nu::shell::column_not_found
× Cannot find column 'UNSET_VARIABLE'
╭─[entry #11:1:5]
1 │ cat $env.UNSET_VARIABLE
· ─────────┬─────────┬
· │ ╰── value originates here
· ╰── cannot find column 'UNSET_VARIABLE'
╰────
Wenn wir auf eine Fallback-Variable zurückgreifen wollen, können wir das default
Schlüsselwort explizit verwenden:
cat ($env.UNSET_VARIABLE? | default "some-default-value")
output:
cat: some-default-value: No such file or directory
Ergänzende Werkzeuge und Ressourcen
Prompting
Nushell unterstützt das Prompting-Tool Starship. Starship bietet eine Reihe von Modulen, die im eigenen Prompt eingebunden werden können.
So sieht mein Prompt aus, inspiriert vom Powerlevel10k-ZSH-Theme:
Resources
Es gibt ein von der Community gepflegtes GitHub-Repository namens nu_scripts, das viele Skripte zur Erweiterung von Nushell enthält.
Das Nushell-Projekt selbst bietet hervorragende Dokumentation zur Shell und zur Nu-Skriptsprache.
Fazit
Meiner Meinung nach ist das Verhalten von Nushell deutlich vorhersehbarer und besser wartbar. Wenn mir ein Fehler beim Schreiben eines Skripts unterläuft, erhalte ich sofort Rückmeldung – anstatt dass zufällig etwas in einer CI/CD-Pipeline oder einem Deployment kaputt geht. Nushell bringt viele eingebaute Werkzeuge mit, z. B. einen HTTP-Client, Parser für diverse Objekt-Notationen oder Tools zur Systemabfrage. Es gibt auch eine MUSL-basierte Standalone-Version von Nushell, die ideal für DevOps-Umgebungen ist, da sie keine zusätzlichen Abhängigkeiten außer dem Linux-Kernel benötigt. Darüber hinaus ist Nushell plattformübergreifend – es funktioniert auch unter Windows und Mac OS X.