Shelltools in Rust – was zum Teufel?
Etablierte Kommandozeilenwerkzeuge für Linux und Unix wie ls oder grep existieren als Nachbau neuerdings auch in Rust, und mancher verbindet mit der Reimplementierung große Hoffnung in Sachen Komfort, Performance und Robustheit. Was motiviert die Macher dieser Werkzeuge, wie praktisch sind sie im Alltag und lohnt sich für passionierte Admins ein Blick über den Tellerrand?
Wenn Filme- und Serienmacher Hacker bei der Arbeit zeigen, muss dafür üblicherweise der Quelltext von Programmen oder die Kommandozeile herhalten, auf der manchmal sogar Befehle wie ip mit der korrekten Ausgabe zu sehen sind. Passionierte Systemadministratoren können da nur lachen – denn natürlich wissen sie, dass hier nichts Sinistres vonstattengeht, sondern dass Werkzeuge wie ls, du oder grep einfach Bestandteil der Arbeit sind.
Die meisten Administratoren haben die gängigen Befehle so weit verinnerlicht, dass sie unmittelbar nach dem Öffnen einer Shell oder nach dem Aufbau einer SSH-Verbindung quasiautomatisch ein paar CLI-Befehle absetzen: ls, um zu schauen, welche Dateien lokal vorhanden sind, ps, um zu erfahren, was auf dem System los ist, w, um die gerade eingeloggten Nutzer zu zeigen, und so weiter.
Etwas Seltsames allerdings geht seit einer Weile vor sich im Land der Kommandozeile: Eine kleine Gruppe von Enthusiasten ist dabei, Rust-Versionen der gängigen CLI-Werkzeuge zu bauen und die alteingesessenen Werkzeuge durch diese neuen Versionen zu ersetzen. Modernes Teufelszeug, wirft mancher Administrator da intuitiv ein. Wer dem Thema so begegnet, liefert aber eine zumindest unterkomplexe Analyse der Situation.
Muss man das ernst nehmen?
Was als akademisches Projekt für Rust-Eiferer begonnen haben mag, hat sich nämlich längst zu einer ernst zu nehmenden Bewegung entwickelt, und die Rust-Versionen der CLI-Standardwerkzeuge sind längst mehr als einfache Klone. So verweisen die Entwickler von Tools wie ripgrep statt grep, eza statt ls und procs statt ps beispielsweise darauf, dass Rust als extrem effiziente Programmiersprache gilt, die für den Bau von CLI-Werkzeugen wie geschaffen sei. Gerade wer sich mit grep, sed oder awk schon mal durch größere Datenmengen gewühlt hat, weiß, dass das schnell eine zeitraubende Angelegenheit wird. ripgrep, ruplacer und frawk sollen hier durch deutliche Performancezugewinne punkten.
Obendrein ist Rust für sein robustes Memory Handling bekannt, sodass typischerweise aus mangelhafter Speicherverwaltung resultierende Sicherheitslecks und Stabilitätsprobleme erst gar nicht entstehen können. Zugegeben: Bugs in ls oder ps lassen sich selten ausnutzen, um Systeme zu übernehmen. Unmöglich ist das aber nicht. Bei Rust, so die Rust-Fans, sei die Wahrscheinlichkeit dafür viel geringer.
Grund genug, sich die verfügbaren Rust-Klone bestehender CLI-Werkzeuge einmal genauer anzuschauen und dabei über den Elefanten zu sprechen, der offensichtlich im Raum steht: Versenken die Rust-Fans viel Zeit in ein Hobby, das außerhalb der Rust-Gemeinde niemandem nützt – oder können die in Rust verfassten Alternativen zu klassischen CLI-Werkzeugen einen tatsächlichen Mehrwert gegenüber ihren etablierten Vorfahren bieten?
Quer durchs Gemüsebeet
Um es gleich am Anfang klar zu sagen: Nur die ganz harten Rust-Fans wünschen sich, dass das Gros der Admins seine lieb gewonnenen Shell-Utils wegwirft und ab sofort stattdessen exklusiv mit den jeweiligen Rust-Varianten arbeitet. Das dürfte in den meisten Fällen schon deshalb auch praktisch unmöglich sein, weil es die gängigen CLI-Werkzeuge schon so lange gibt, dass ihre Nutzung wie beschrieben vielen Admins längst in Fleisch und Blut übergegangen ist. Die Muße, sich eine ganze Weile dazu zu zwingen, statt ps lieber procs einzugeben, bis dieses ebenso fast schon unbewusst den Weg durch die Finger auf die Tastatur findet, dürften die wenigsten Admins haben.
Das bedeutet aber nicht, dass die in Rust verfassten Alternativen zu den gängigen CLI-Werkzeugen sich zu bestimmten Einsatzzwecken oder zu besonderen Anlässen nicht hervorragend als Ergänzung zu ihren Gegenstücken in C verwenden ließen. Und eben darauf läuft es in den meisten Fällen auch erst mal hinaus. Aus Sicht interessierter Admins stellt sich damit vor allem die Frage, welche bekannten Tools überhaupt Rust-Alternativen besitzen und was diese leisten. Ein paar Rust-Verfechter haben zu diesem Thema übersichtliche Tabellen zusammengetragen, die einen guten ersten Eindruck der Wahlmöglichkeiten bieten (siehe ix.de/zmcw). Und diese langen Listen machen schnell deutlich: Den Rust-Enthusiasten ist es mit ihrem Anliegen durchaus ernst (siehe auch Tabelle „Rust-Tools und ihre C-Pendants“).
| Rust-Tools und ihre C-Pendants | ||
| Rust-Tools¹ | vergleichbares C-Pendant | Beschreibung |
| bartib | timewarrior | Zeitmanagmenttool |
| bat | cat² | Dateiinhalt anzeigen mit Syntaxhighlighting und anderen Features |
| bottom | top | anpassbarer grafischer Prozess- und Systemmonitor |
| choose | cut², awk | schneidet Abschnitte aus Textzeilen; schnell, anwenderfreundlich |
| counts | sort² | uniq² -c | zählt Zeilen und sortiert sie nach Häufigkeit |
| delta | diff³ | diff mit Syntaxhighlighting und Side-by-Side-Ansicht |
| diskonaut | du | Navigator durch die Festplattenbelegung mit Aufräumfunktion |
| dua | du² | schlankes, benutzerfreundliches du |
| dust | du² | benutzerfreundliches du mit Baumansicht |
| dutree | du² | benutzerfreundliches du mit Baumansicht und weiteren Features |
| dysk | df² | benutzerfreundliche, umfangreiche Anzeige der Festplattenbelegung |
| erdtree | du² | umfangreiches du mit Ansicht wie bei ls -l, tree oder du |
| eza | ls² | umfangreiche Anzeige des Verzeichnisinhalts |
| fclones | – | findet und entsorgt doppelte Dateien |
| fd | find⁴ | schlankes, benutzerfreundliches Finden von Dateien |
| frawk | awk | performante awk-ähnliche Skriptsprache |
| fselect | – | Find mit SQL-ähnlicher Syntax |
| grex | regexgen (JS) | erstellt reguläre Ausdrücke anhand von Beispieleingaben |
| hck | cut², awk, xsv | cut mit scharfer Klinge |
| huniq | sort² | uniq² | löscht oder meldet mehrfach vorkommende Zeilen aus einer sortierten Datei |
| hyperfine | time | umfangreiches und anwenderfreundliches Benchmarkwerkzeug |
| legdur | – | erzeugt Hashes ganzer Verzeichnisinhalte und vergleicht sie mit vorherigen |
| lsd | ls² | ls deluxe mit vielen Formatierungsoptionen und Icons |
| macchina | – | zeigt Systeminformationen an; übersichtlich, anpassbar |
| mprocs | – | startet parallele Prozesse |
| natls | ls² | modernes ls |
| navi | tldr | interaktives Spickzetteltool |
| ouch | – | Metatool für die gängigen Kompressionstools |
| pastel | – | Farbenverwaltung |
| pdu | du² | parallelisiertes, benutzerfreundliches du |
| pipr | – | Piping-Assistent mit Hilfe- und Ausgabevorschaufenster |
| please, pleaseedit | sudo | erweitertes sudo mit Unterstützung regulärer Ausdrücke |
| procs | ps | moderner Ersatz für ps, zeigt Prozessinformationen an |
| pueue | – | Manager für long-running Tasks |
| rargs | xargs | erweiterte Befehlsausführung |
| rip | rm² | rm Improved mit Friedhof und Inspektion |
| ripgrep (rg) | grep, ack | rekursives grep mit Ausnahmen |
| rnr | rename | Batch-Werkzeug zum sicheren Umbenennen mehrerer Dateien und Verzeichnisse |
| sad | sed | moderner Stream-Editor |
| samply | – | Prozess- und Thread-Profiler |
| sd | sed | benutzerfreundliches sed |
| skim (sk) | fzf | Klon des Fuzzy Finder |
| spacer | – | fügt Trennbalken in Ausgabeunterbrechungen ein |
| stringsext | strings | sucht nach Multi-Byte-codierten Strings in Binaries |
| systeroid | sysctl | erweitertes und komfortables Tool zur Konfiguration von Kernelparametern zur Laufzeit |
| tailspin | – | Logfile-Highlighter |
| tealdeer | tldr | Spickzetteltool |
| tin-summer (sn) | du² | benutzerfreundliches du mit Ausnahmedefinition |
| topgrade | – | Metapaketmanager |
| xcp | cp² | erweitertes, komfortables Kopieren |
| youki | runc | Container-Runtime |
| zet | uniq², comm² | findet Unterschiede, Dubletten, Teil- und Vereinigungsmengen in Dateien und führt sie zusammen |
| zoxide | cd, z, autojump | Verzeichnis wechseln mit Gedächnis |
| ¹ Die Links zu den einzelnen Tools sind unter: ix.de/zmcw zu finden; ² auch als uutils-coreutils; ³ auch als uutils-diffutils; ⁴ auch als uutils-findutils | ||
Entsprechend umfangreich ist die Liste der Standardwerkzeuge, die mittlerweile auch in Rust zur Verfügung stehen. Naturgemäß stellt sich den Entwicklern dabei eine Herausforderung: Wie sollen die Werkzeuge heißen? Ein echtes Drop-in Replacement würde einen identischen Namen voraussetzen. Das verbietet sich auf den meisten Systemen aber, weil es ansonsten unvermeidbar zu einem Chaos aus C- und Rust-Werkzeugen käme. Die allermeisten Autoren von Rust-Werkzeugen haben sich also für einen Mittelweg entschieden: Die Werkzeuge selbst heißen zwar anders, beherrschen im Wesentlichen aber dieselben Kommandozeilenparameter wie das jeweilige Ursprungswerkzeug aus der GNU-Kollektion.
Auch das ist an dieser Stelle ja durchaus noch zu beachten: Dass cp, ls und Konsorten nämlich nicht nur in der – unter Linux üblicherweise genutzten – Version des GNU-Projekts zur Verfügung stehen, sondern ihrerseits eigentlich ein Nachbau der jeweiligen BSD-Kommandos sind. Die unterstützten Parameter unterscheiden sich zwischen den BSD-Varianten und den GNU-Versionen dabei heute oft noch im Detail. Wer beispielsweise regelmäßig auf macOS arbeitet und dort das Terminal nutzt, hat die zu Darwin gehörenden Kommandozeilenwerkzeuge vorinstalliert, die von den BSD-Versionen abstammen. Tools wie grep und ps quittieren einen Aufruf, der auf einem Linux-System klappen würde, dann oft bloß mit einer Fehlermeldung.
Damit das Chaos sich nicht in die Rust-Welt fortsetzt, entscheiden sich die meisten Rust-Entwickler zumindest für teilweise Kompatibilität mit den jeweiligen GNU-Werkzeugen. Das bedeutet auch, dass es in Skripten recht einfach ist, von der C-Variante auf die Rust-Variante umzustellen. Denn ein Kommando wie ls -la wird dann einfach zu eza -la, ansonsten ändert sich aber kaum etwas (siehe Abbildung 1). Überhaupt ist eza ein gutes Beispiel dafür, dass sich mit den Rust-Nachbauten der gängigen CLI-Werkzeuge sinnvoll arbeiten lässt.
Manchen Rust-Entwicklern geht es allerdings um echte Drop-in Replacements. Mit den uutils-coreutils steht deshalb ein in Rust verfasster Werkzeugsatz zur Verfügung, der vollständige CLI-Kompatibilität zu den GNU-Werkzeugen erreichen will und jede Abweichung davon als Bug betrachtet. Hier geht es tatsächlich darum, lieb gewonnene Befehle wie ls durch ein Rust-Pendant zu ersetzen (siehe Abbildung 2 und das Interview auf Seite 58).
Ruckelige Installation
Bevor sich Werkzeuge wie eza in ihrer ganzen Schönheit verwenden lassen, offenbart ihre Installation allerdings eine der großen Schwachstellen, an denen die Rust-Werkzeuge derzeit leiden. Zumindest jene, die nicht Teil der uutils-coreutils sind. Denn es gibt keine fertige, leicht installierbare Sammlung all jener Werkzeuge, die Standard-CLI-Befehle in Besser mit anderem Namen nachbauen wollen. Jedes Werkzeug kocht stattdessen sein eigenes Süppchen, und will man alle Rust-Substitute für die gängigen CLI-Kommandos zusammentragen, ist das eine aufwendige und zeitraubende Tätigkeit (Eine möglichst vollständige Linksammlung findet sich unter ix.de/zmcw).
Für eza, also den ls-Ersatz in Rust, existiert etwa in Arch Linux eine Integration in Pacman. Für Debian GNU/Linux und die aktuellen Ubuntu-Versionen steht ein PPA-Verzeichnis zur Verfügung. In Gentoo gibt es einen Emerge-Port. Fedora legt eza als RPM-Paket bei, das sich mit dnf einfach installieren lässt. Unter macOS stellt Homebrew eine Variante des Programms bereit.
Mühsame Suche
Wer sich eza so aufs System holt, hat eben nur eza, aber noch kein einziges der vielen anderen für den Alltag nötigen Werkzeuge. Wer procs will, navigiert erst mal zu dessen GitHub-Verzeichnis und sucht die Installationsanweisungen für das eigene Betriebssystem oder die Distribution heraus. Wer das für die zwölf Standardwerkzeuge, die der Nutzer STS10 auf GitHub zusammengetragen hat, praktiziert, hat bald keine Lust mehr.
Erschwerend kommt hinzu, dass es für manche GNU-Werkzeuge nicht nur einen Rust-Ersatz gibt, sondern mehrere konkurrierende. Für diff etwa listet die erwähnte GitHub-Seite delta und difftastic als mögliche Alternativen. Noch bunter wird es bei du: Hier buhlen diskonaut, dua, dust, dutree, erdtree, pdu und tin-summer um die Gunst der Nutzer. Auch für ls steht eine Alternative zu eza in Form von lsd zur Verfügung. Und manche essenziellen Shellwerkzeuge fehlen in der Liste vollständig, etwa ein adäquater, sprich kompatibler Ersatz für awk.
Wieder andere alternative Rust-Implementierungen bekannter Shellutils sind im Netz zwar zu finden, fehlen aber auf der kuratierten Liste. Im Zweifelsfall muss der Admin also mehrere Varianten von Rust-CLI-Alternativen vergleichen und die für ihn beste, am leichtesten zu installierende oder flotteste Alternative herausfinden. Komfortabel ist das nicht, und es verleidet die Vorfreude auf den Einsatz der Rust-Werkzeuge erheblich.
Nicht immer kompatibel
Hat man sich durch den Dschungel der Alternativen und ihre Installationsvorgänge gekämpft, steht im Idealfall eine lokale Sammlung von Rust-basierten Alternativen zu den GNU- oder BSD-Varianten des Betriebssystems parat. Bei eza beispielsweise fällt auf, dass eza -la nicht klaglos als echtes Drop-in Replacement für ls funktioniert. Die Ausgaben sind sehr ähnlich, aber nicht deckungsgleich. Shellskripte etwa, die die Ausgabe von ls interpretieren wollen, würden zumindest bei eza also schon auf die Nase fallen.
Auf macOS fällt obendrein auf, dass von der versprochenen Geschwindigkeit durch Rust nicht allzu viel zu spüren ist. Im Gegenteil: Eine flotte – nicht professionelle – Messung mit time ergibt, dass eza -la fast doppelt so lange dauert wie ls -la, sprich 0,2 Sekunden gegenüber 0,1 Sekunden. Bei rekursiven Durchläufen mit -laRS in großen, tiefen Verzeichnissen wurden die Unterschiede noch deutlicher (siehe Tabelle „ls -laRS vs. uls -laRS vs. eza -laRS“). Auf Ubuntu 24.04 waren die Zeiten von eza und ls beinahe identisch – hier liefen die Werkzeuge aber auch auf x86_64 und nicht auf AARCH64.
| ls -laRS vs. uls -laRS vs. eza -laRS (macOS) | ||
| Tools | 1. Lauf | 2. Lauf |
| ls | 7,709 s | 5,311 s |
| uls | 1,475 s | 1,315 s |
| eza | 19,420 s | 17,945 s |
Noch mal etwas anders gestaltet sich die Erfahrung mit procs, dem ps-Ersatz in Rust. Hier wird schnell klar, dass den Entwicklern die Eigenschaft ihres Werkes, als Drop-in-Ersatz für das klassische ps zu fungieren, nicht sonderlich wichtig war. procs -auxw beispielsweise schlägt fehl. procs -a funktioniert, fördert anders als ps a aber nicht eine Liste aller Prozesse des Nutzers auf den Bildschirm, sondern eine Art interaktive Ansicht (siehe Abbildung 3). Sie erinnert eher an die Ausgabe von top. Das wirft procs zumindest als unmittelbaren Ersatz für ps aus dem Rennen.
Eigene Stärken
Zudem kennt das Werkzeug diverse CLI-Parameter nicht, die Administratoren im Alltag unter Linux mit den GNU-Werkzeugen regelmäßig nutzen. ps fauxw beispielsweise zeigt einen Baum der gestarteten Prozesse des Systems mit diversen Zusatzinformationen an. procs aber unterstützt von diesen Parametern nur -a. Eine Baumansicht lässt sich mittels -t erreichen, viele andere ps-Parameter sind gar nicht implementiert. Weder für Shellskripte noch für die alltägliche Nutzung kann bei procs also von einem Eins-zu-eins-Ersatz die Rede sein.
Arbeitet man mit der Riege der In-etwa-Nachbauten, lässt sich das Spiel beliebig wiederholen. difftastic etwa kommt ebenfalls mit Pager-Funktion daher wie procs, beherrscht viele CLI-Parameter von diff nicht und ist beim Aufruf mit diesem entsprechend nicht kompatibel.
Dabei soll an dieser Stelle nicht der Eindruck entstehen, als seien die Rust-Programme, die als Ersatz für die klassischen Shelltools antreten, wegen ihrer teils unvollständigen oder gar nicht gegebenen Kompatibilität zu den historischen Vorbildern schlechter oder minderwertig. Ganz im Gegenteil: Manche Rust-Programme liefern Funktionen, die man sich von den klassischen Vorbildern wünscht.
Der du-Ersatz dua ist dafür ein gutes Beispiel: Dessen Ausgabe ist auch ganz ohne die Angabe von Parametern deutlich übersichtlicher und schicker als jene des normalen du. Will man etwa eine Übersicht der Dateien im lokalen Ordner haben, sortiert nach ihrer Größe und aufsteigend, geht das auf der Shell beispielsweise mit
du -sh ./* 2> /dev/null | sort -h
(siehe Abbildung 4). Oder eben mit dua ohne Parameter (siehe Abbildung 5): Das liefert nicht nur eine übersichtliche farbige Liste, sondern zeigt während der Erhebung der Daten auch den Fortschritt an. Beim klassischen du wartet man stattdessen vor einer leeren Anzeige. Richtig komfortabel wird dua, wenn man es mit dua i im interaktiven Modus startet und damit nicht nur die Festplatte oder SSD durchstöbern, sondern bei der Gelegenheit gleich aufräumen kann. Das Beispiel zeigt: Die Rust-Werkzeuge liefern an vielen Stellen echten Mehrwert.
Coreutils in Rust – als echter Ersatz gedacht
Wer auf der Suche nach einem echten Ersatz für die C-Shellutils ist, muss stattdessen zu den Coreutils des uutils-Projektes wechseln (siehe Tabelle „GNU Coreutils vs. uutils-coreutils“ und ix.de/zmcw). Doch ist hier Vorsicht angesagt: Wer die Werkzeuge installiert, führt tatsächlich die Rust-Werkzeuge aus, wenn er Befehle wie cp oder ls aufruft. Manche Paketmanager umgehen das Problem, indem sie die Tools in einen Pfad außerhalb von $PATH legen und in den Aufrufpfad dann Wrapper packen, die u<Programmname> heißen, oder gleich als u<Programmname> ablegen. Auf macOS unter Homebrew wird aus ls so etwa uls.
| GNU Coreutils vs. uutils-coreutils | ||||
| GNU Coreutils (Debian) | uutils-coreutils | Rust-Geschwister | Namensauflösung | Beschreibung |
| arch | arch | – | architecture | zeigt die Prozessorarchitektur an |
| – | b2sum | – | BLAKE2 checksum | erzeugt und prüft BLAKE2-Checksummen |
| – | b3sum | – | BLAKE3 checksum | erzeugt und prüft BLAKE3-Checksummen |
| – | base32 | – | base32 | konvertiert in oder von Base32-ASCII-Text |
| base64 | base64 | – | base64 | konvertiert in oder von Base64-ASCII-Text |
| basename | basename | – | base part of pathname | entfernt Pfad und optional ein Suffix aus einem vollständigen Dateinamen |
| – | basenc | – | base encoding | konvertiert in oder von ASCII-Text |
| cat | cat | bat | concatenate files | verkettet Dateien oder gibt sie auf stdout aus |
| chcon | chcon¹ | – | change context | ändert den SE-Linux-Security-Kontext |
| chgrp | chgrp² | – | change group | ändert die Gruppe von Dateien oder Verzeichnissen |
| chmod | chmod² | – | change mode | ändert die Berechtigungen von Dateien oder Verzeichnissen |
| chown | chown² | – | change owner | ändert den Besitzer von Dateien oder Verzeichnissen |
| chroot | chroot² | – | change root directory | führt einen Befehl mit einem bestimmten Stammverzeichnis aus |
| cksum | cksum | – | checksum | berechnet Prüfsummen und die Anzahl der Bytes von Dateien |
| comm | comm | – | compare and merge | vergleicht zwei sortierte Dateien zeilenweise |
| cp | cp | xcp | copy | kopiert Dateien oder Verzeichnisse |
| csplit | csplit | – | context split | teilt Dateien abhängig vom Inhalt in mehrere Teile |
| cut | cut | choose, hck | cut parts of a file | entfernt Abschnitte aus jeder Zeile einer Datei |
| date | date | – | system date time | zeigt oder setzt die Systemzeit |
| dd | dd | – | dump data | konvertiert und kopiert Dateien |
| df | df | dysk | disk space free | zeigt den freien Speicherplatz eines Dateisystems an |
| dir | dir | – | directory | wie ls – listet Verzeichnisse und ihre Inhalte auf |
| dircolors | dircolors | – | directory entry colors | definiert Farben für ls |
| dirname | dirname | – | directory part of pathname | zeigt nur den Pfad eines vollständigen Dateinamens an |
| du | du | dua, dust, dutree, erdtree, pdu | disk usage | zeigt die Speicherplatzbelegung in einem Dateisystem |
| echo | echo | – | echo stdin | gibt einen Text aus |
| env | env | – | environment settings | setzt Umgebungsvariablen und führt Programme aus |
| expand | expand | – | expand white space | konvertiert Tabulatorzeichen in Leerzeichen |
| expr | expr | – | evaluate expression | wertet Ausdrücke aus |
| factor | factor | – | evaluate prime factor | berechnet Primfaktoren |
| false | false | – | false | liefert einen Fehler als Rückgabewert zurück |
| flock | – | – | file lock | verwaltet Datei-Locks |
| fmt | fmt | – | format text | einfacher Textformatierer |
| fold | fold | – | fold | bricht Zeilen auf eine bestimmte Länge um |
| groups | groups² | – | groups of a user | zeigt die Gruppen an, denen ein Benutzer angehört |
| – | hashsum | – | hashsum | erzeugt und prüft Checksummen |
| head | head | – | head | zeigt nur den Anfang einer Datei an |
| hostid | hostid² | – | host identifier | zeigt die hexadezimale ID des Rechners an |
| – | hostname | – | hostname | setzt oder zeigt den Namen des Rechners an |
| id | Id² | – | user’s identity | zeigt die Nutzer- und Gruppeninformationen eines Benutzers an |
| install | install² | – | install | kopiert Dateien und Verzeichnisse und setzt Attribute |
| join | join | – | join lines of two files | vereinigt Zeilen aus zwei Dateien mit einem gemeinsamen Feld |
| – | kill² | – | kill process | sendet Signale an Prozesse |
| link | link | – | link to a file | erstellt einen Hardlink auf eine Datei |
| ln | ln | – | link node | erstellt Hard- und Softlinks zu Dateien oder Verzeichnissen |
| logname | logname² | – | login name | zeigt den Loginnamen des Nutzerkontos an |
| ls | ls | exa, lsd, nat | list directory content | listet Verzeichnisse und ihre Inhalte auf |
| md5sum | md5sum | – | MD5 checksums | berechnet und vergleicht MD5-Prüfsummen |
| mkdir | mkdir | – | make directory | erstellt Verzeichnisse |
| mkfifo | mkfifo² | – | make ‘first in first out’ | erstellt Pipes (FIFOs) |
| mknod | mknod² | – | make node | erstellt Gerätedateien |
| mktemp | mktemp | – | make temporary file or directory | erstellt temporäre Daten oder Verzeichnisse |
| – | more | – | more | zeigt den Inhalt einer Datei an |
| mv | mv | – | move | verschiebt Dateien oder Verzeichnisse respektive benennt sie um |
| nice | nice² | – | be nice | ändert die Priorität eines Prozesses |
| nl | nl | – | number lines | fügt einer Datei Zeilennummern hinzu |
| nohup | nohup² | – | no hang up | führt einen Befehl aus, der auch nach dem Abmelden weiterläuft |
| nproc | nproc | – | number of cores for a process | zeigt die für einen Prozess verfügbare Zahl der CPU-Kerne an |
| numfmt | numfmt | – | number format | konvertiert Zahlen in menschenlesbare Formate |
| od | od | – | octal dump | listet den Inhalt von Dateien in verschiedenen Formaten auf, etwa hexadezimal |
| paste | paste | – | paste lines of files | vereinigt Zeilen von Dateien |
| pathchk | pathchk² | – | path check | prüft Dateinamen auf Portabilität |
| pinky | pinky² | – | Pinky – lightweight finger | zeigt Informationen zu einem Benutzer an |
| pr | pr | – | printing format | formatiert Dateien zum Drucken |
| printenv | printenv | – | print environment settings | zeigt Umgebungsvariablen an |
| printf | printf | – | print formated | gibt Daten formatiert aus |
| ptx | ptx | – | permuted index | erstellt einen permutierten Index von Dateiinhalten |
| pwd | pwd | – | print working directory | zeigt das aktuelle Arbeitsverzeichnis an |
| readlink | readlink | – | read link | zeigt Informationen zu einer symbolischen Verknüpfung an |
| realpath | realpath | – | real path | zeigt den tatsächlichen Pfad an |
| rm | rm | rip | remove | löscht Dateien |
| rmdir | rmdir | – | remove directory | löscht Verzeichnisse (auch rekursiv) |
| runcon | runcon¹ | – | run with context | führt einen Befehl in einem bestimmten SE-Linux-Kontext aus |
| seq | seq | – | sequence | gibt eine Folge von Zahlen aus |
| sha*sum | sha*sum | – | SHA* checksums | berechnet und vergleicht verschiedene SHA*-Prüfsummen |
| shred | shred | – | shred file | löscht Dateien unwiederbringlich durch Überschreiben |
| – | shuf | – | shuffle | mischt Zeilen durch |
| sleep | sleep | – | sleep | wartet eine bestimmte Zeit |
| sort | sort | – | sort lines of a file | sortiert Zeilen einer oder mehrerer Dateien |
| split | split | – | split file content | teilt eine Datei in Stücke auf |
| stat | stat² | – | state of inode | gibt Dateisysteminformationen zu einer Datei aus |
| – | stdbuf² | – | standard streams buffer | verändert die Buffer von stdin, stdout und stderr für ein Kommando |
| stty | stty² | – | set teletype | setzt Terminaleinstellungen |
| sum | sum | – | checksum | zeigt Prüfsummen und Anzahl der Blöcke einer Datei an |
| sync | sync | – | synchronize data | synchronisiert die Dateisystempuffer mit dem Massenspeicher |
| tac | tac | – | reverse cat | schreibt die Zeilen einer Datei in umgekehrter Reihenfolge auf stdout |
| tail | tail | – | show tail | zeigt nur das Ende einer Datei an |
| tee | tee | – | tee connector | zweigt den Datenstrom in eine Datei ab |
| test, [ ] | test, [ ] | – | test | prüft Dateitypen und wertet Ausdrücke aus |
| timeout | timeout² | – | timeout | begrenzt einen Befehl auf ein Zeitfenster |
| touch | touch | – | touch file attributes | ändert Zeitstempel von Dateien und Verzeichnissen und legt leere Dateien an |
| tr | tr | – | translate characters | ersetzt oder löscht Zeichen in einer Datei |
| true | true | – | true | liefert wahr als Rückgabewert |
| truncate | truncate | – | truncate | verändert die Größe einer Datei |
| tsort | tsort | – | topologic sort | führt eine topologische Sortierung aus |
| tty | tty² | – | teletype | zeigt den Namen des Terminals an |
| uname | uname | – | unix name | zeigt Informationen zum Betriebssystem an |
| unexpand | unexpand | – | unexpand tabs | konvertiert Leerzeichen in Tabulatorzeichen |
| uniq | uniq | huniq | show unique lines | löscht oder meldet mehrfach vorkommende Zeilen aus einer sortierten Datei |
| unlink | unlink | – | unlink | löscht eine Datei mit der unlink-Funktion |
| – | uptime | – | system up time | zeigt die uptime des Systems, die Zahl der Nutzer und Jobs an |
| users | users² | – | users | zeigt die derzeit am Rechner angemeldeten Benutzer an |
| vdir | vdir | – | variant of dir | Variante von dir |
| wc | wc | – | word count | zählt die Bytes, Wörter und Zeilen einer Datei |
| who | who² | – | who | zeigt Informationen über die derzeit am Rechner angemeldeten Benutzer an |
| whoami | whoami | – | who am i | zeigt die eigene Benutzeridentität an |
| yes | yes | – | yes | gibt wiederholt einen Text aus, per default „y“ |
| ¹ nur Linux; ² nur Linux und macOS (kein Windows) | ||||
Wer tatsächlich dauerhaft auf die Rust-Werkzeuge umsteigen möchte, fügt im ersten Fall den Ordner /usr/local/opt/uutils-coreutils/libexec/uubin der Liste der Pfade hinzu, in denen die Shell nach Programmen sucht. So oder so gilt aber: Wer die Rust-Version der Coreutils installieren möchte, tut vermutlich gut daran, zunächst in einer virtuellen Instanz zu experimentieren.
Im Test haben sich die Werkzeuge des Coreutils-Pakets von uutils dabei durchaus bewiesen. Das gilt zunächst für die CLI-Kompatibilität mit den GNU-Werkzeugen: Sämtliche Parameter für ls existieren in der Rust-Version und führen zur selben Ausgabe wie beim C-Gegenstück. Die Ausgabe von df ist identisch und lässt sich durch verschiedene Parameter steuern.
Flott unterwegs
Auch in Sachen Geschwindigkeit konnten die Werkzeuge aus der Coreutils-Sammlung von uutils punkten. Schnelle Messungen mit time ergaben, dass etwa der Aufruf von ls -ltR deutlich langsamer war als jener von uls -ltR. Hier betrug der Unterschied bei einem großen Verzeichnis mit vielen Unterverzeichnissen mehr als eine Sekunde (siehe auch Tabelle „ls -laRS vs. uls -laRS vs. eza -laRS“).
Freilich spielt der Kernel des Systems zusammen mit dem genutzten Dateisystem eine große Rolle bei der Frage nach der Dauer der Ausführung solcher Befehle. Zumindest kamen beim Schnelltest in der Redaktion aber dieselben Ordner und Kommandos zur Anwendung, um sowohl die klassischen Coreutils in C als auch ihre Rust-Pendants zu testen. Obendrein gelten die Vorteile auch nicht bei allen Programmen.
Wer die Coreutils in Rust ausprobieren möchte, findet im Netz für die meisten Distributionen fertige Pakete oder eine andere Form der Integration – auch für Debian GNU/Linux, und das ist alles andere als ein Zufall. Denn hinter den uutils-coreutils steht Sylvestre Ledru, Präsident der Mozilla-Foundation, Debian-, Ubuntu-, LLVM-, Clang-Entwickler und Rust-Fan, der die Arbeit an der Werkzeugsammlung während der Coronapandemie begann.
In der Community verankert
In einem Vortrag während der FOSDEM 2023 erklärte er die Beweggründe: Neben den erwähnten Vorteilen in Sachen Sicherheit sei Rust vor allem extrem gut portierbar. Obendrein stünden in Rust nativ viele Funktionen zur Verfügung, die man in C erst mühsam nachbauen muss. Die walkdir-Funktion etwa ermöglicht das Iterieren durch Dateisystembäume und ist die perfekte Grundlage für Werkzeuge wie cp oder rm. Einen großen Teil ihrer Geschwindigkeitsvorteile erreichen die uutils-coreutils, indem sie auf eben diese Funktionen zurückgreifen. In Form von lscolors steht – das ist fast schon witzig – in Rust sogar eine Funktion bereit, die die Ausgabe in dieselben Farben tunkt wie das ls aus den GNU Coreutils.
Sein ursprüngliches Ziel, ein Debian-System auf Basis seiner Rust-Werkzeuge zum reibungslosen Start zu bringen, hat Ledru dabei schon längst erreicht. Noch betrachtet er die Entwicklung aber nicht als abgeschlossen – noch bessere Kompatibilität zu den Coreutils sei das Ziel, und ein Quäntchen Performance sei hier und dort wohl auch noch aus den Werkzeugen herauszupressen. Wer die uutils-coreutils installiert, merkt im Alltag jedenfalls nur noch sehr selten, dass er nicht die GNU-Version der Werkzeuge nutzt.
Auch lizenzrechtlich haben die grundsätzlich sehr strengen Debian-Entwickler an den uutils-coreutils nichts auszusetzen, denn Ledru, immerhin ja einer der Ihren, hat sein Werk unter die MIT-Lizenz gestellt, die kaum irgendeine Art und Weise der Verwendung ausschließt. Ähnlich verhält es sich übrigens auch mit jenen Werkzeugen, die nicht zu den uutils-coreutils gehören: Hier findet sich zwar ein recht bunter Strauß an Lizenzen, ihnen allen ist aber gemein, dass sie als frei im Sinne der FSF, des GNU-Projekts und der Debian Free Software Guidelines gelten.
Wer die Rust-Variante der Coreutils ausprobieren möchte, findet auf der Website des Projekts dafür die nötigen Informationen (siehe ix.de/zmcw). Der einzige Wermutstropfen: Zu den Coreutils gehören alltägliche Werkzeuge wie ls und cp, das allseits beliebte grep fehlt aber ebenso wie ein Nachbau von awk oder sed. Hier muss man im Zweifelsfall also doch wieder auf Werkzeuge aus der Community zurückgreifen, deren Ziel nicht die vollständige Kompatibilität mit den jeweiligen GNU-Varianten der Programme ist – wie etwa bei procs oder eza.
Kleines Trostpflaster: Immerhin arbeitet das uutils-Projekt an zwei weiteren Sammlungen: Noch ganz am Anfang stehen die uutils-diffutils, und von den avisierten Werkzeugen find, locate, updatedb und xargs der GNU Findutils existieren zumindest schon Rust-Nachbauten von find und xargs.
Fazit
Perfekt komfortabel bettet man sich im Land der Rust-Werkzeuge auf der Shellebene bis dato also noch nicht, auch wenn sich die meisten Funktionen bereits durch bestehende Werkzeuge ersetzen lassen. Die Daten und Messwerte, die sich aus den bereits nachgebauten Rust-Werkzeugen ergeben, machen aber Lust auf mehr. Bis es beispielsweise einen Eins-zu-eins-Ersatz für diff gibt, dürfte es nur eine Frage der Zeit sein. Administratoren sollten nicht davor zurückschrecken, neue Pfade zu beschreiten und insbesondere den uutils-coreutils eine Chance zu geben.
Auch bei den In-etwa-Nachbauten und den Neuschöpfungen finden sich interessante, moderne und performante Werkzeuge, die Admins sich wiederholende Aufgaben abnehmen oder diese komfortabler gestalten. Dabei bleiben die Entwickler nicht bei den üblichen Datei- und Prozesswerkzeugen für die Kommandozeile stehen. Moderne Terminal-Emulatoren, Shells, Dateimanager, Kalkulatoren, Netzwerk- und Entwicklerwerkzeuge sowie Editoren und Viewer etwa für Text-, Markdown-, CSV- und Binärdateien können ebenso den Arbeitsalltag erleichtern (siehe Tabellen dazu). Wir werden einige von ihnen in kommenden Heften separat vorstellen. (sun@ix.de)
| Terminal-Emulatoren, Shells und Shellerweiterungen in Rust | ||
| Rust-Programm | vergleichbares C-/C++-/C#-Pendant | Beschreibung |
| Terminal-Emulatoren | ||
| Alacritty | termite | anpassbarer Terminal-Emulator mit vim-Modus, Suche, Multi-Window- und Mausunterstützung |
| Rio | kitty | GPU-beschleunigter Alacritty-Ableger mit hoher Farb- und Bildauflösung |
| sshx | – | kollaboratives Terminal mit HTTPS-Sharing |
| Wezterm | kitty | GPU-beschleunigter Terminal-Emulator und -Multiplexer mit hoher Farb- und Bildauflösung |
| Zellij | tmux | Terminal-Multiplexer |
| Shells | ||
| ion | dash | schnelle, moderne Shell |
| nushell | shell | strukturierte Shell mit erweitertem Piping für den Umgang mit struktierten Daten |
| Shellerweiterungen | ||
| atuin | Ctrl-r | erweiterte Shell-History mit SQLite-Backend und zusätzlichen Kommandoinformationen |
| IntelliShell | – | erweiterte Kommandovervollständigung für Bash, Zsh, Fish und PowerShell, wie IntelliSense für Shells |
| mcfly | Ctrl-r | schnelle Suche in der Shell-History mit Priorisierung der Ergebnisse durch ein kleines neuronales Netz |
| Shellharden | ShellCheck | Bash-Syntaxhighlighter mit semiautomatischer Korrektur |
| starship | Spaceship | anpassbarer Shell-Prompt |
| Dateimanager in Rust | |
| broot | Midnight-Commander-ähnlicher, umfangreicher Dateimanager mit Farben, Dateiviewern, Kommandozeile et cetera |
| felix | schneller, einfacher Dateimanager mit vim-ähnlicher Handhabung |
| joshuto | ranger-ähnlicher Dateimanager |
| xplr | Dateimanager und interaktiver Orchestrator von Dateiwerkzeugen |
| yazi | Dateimanager mit asynchronem Non-blocking-I/O |
| CLI-Rechner in Rust | |
| cpc | Rechner mit Einheitenumrechner |
| eva | bc-Klon mit Syntaxhighlighting und persistenter History |
| fend | Rechner mit Einheitenumrechner |
| kalker | wissenschaftlicher Rechner mit Variablen, Funktionen, Differenzial- und Integralrechnung |
| Netzwerk-, Web- und E-Mail-Helfer in Rust | ||
| Rust-Werkzeuge | vergleichbare C-Pendants | Beschreibung |
| Netzwerkhelfer | ||
| bandwhich | iftop, bmon, tcpmon | Netzwerkauslastungsmonitor |
| dog | dig | DNS-Client |
| drill | jMeter | HTTP-Lasttester |
| lemmeknow | – | String-Analyzer, etwa für Netzwerkpakete oder Malware |
| miniserve | – | schlanker Dateiserver über HTTP mit TLS und HSTS |
| RustScan | – | adaptiver schneller Portscanner |
| Webhelfer | ||
| ffsend | Firefox Send | einfaches und sicheres Datei-Sharing |
| htmlq | sed, awk, grep | HTML-Content-Extrahierer |
| hyperlink | – | schneller URL-Checker |
| mdBook | – | erstellt moderne Onlinebücher in TOML aus Markdown-Dateien |
| monolith | – | bündelt Webseiten in einzelnen HTML-Dateien |
| xh | httpie | performanter HTTP-Client |
| E-Mail-Werkzeuge | ||
| himalaya | – | E-Mail-Client |
| mrml | mjml | Mailjet Markup Language |
| Entwicklungswerkzeuge und -helfer in Rust | ||
| Rust | C-Pendants | Beschreibung |
| deno_lint | ESLint | performanter Linter für JavaScript und TypeScript |
| difftastic | diff | vergleicht Dateien auf Basis der Syntax von über 30 Programmiersprachen |
| dprint | Prettier | erweiterbare und konfigurierbare Codeformatierungsplattform |
| fastmod | codemod | Werkzeug zum Refaktorieren großer Codemengen, sed & diff in großen Verzeichnishierarchien |
| fnm | nvm | einfacher, schneller Node.js-Versionsmanager |
| frum | rbenv | moderner Ruby-Versionsmanager |
| gitcliff | – | anpassbarer Changelog-Generator |
| gitui | lazygit | Terminal UI für git |
| jaq | jq | JSON-Query-Language-Tool |
| jless | – | JSON-Viewer mit Suchfunktion |
| jql | jq | JSON-Query-Language-Tool |
| just | make | Command-Runner mit Überschneidungen zu make |
| ripsecrets | – | Secrets-Scanner für Sourcecode |
| RSLint | ESLint | performanter Linter für JavaScript und TypeScript |
| ruff | – | performanter Linter für Python |
| ruplacer | – | sucht und ersetzt Text in Dateien |
| tokei | cloc | zählt Code |
| volta | nvm | schneller JavaScript-Tool-Manager |
| watchexec | – | automatischer Command-Runner für Unit-Tests und Linter oder Syntax-Checker |
| Editoren und Viewer | ||
| Rust | bekanntestes C-Pendant | Beschreibung |
| Text und Code | ||
| amp | vim | durch vim inspirierter Texteditor |
| helix | kakoune/neovim | durch Kakoune/Neovim inspirierter Texteditor |
| kibi | kilo | minimalistischer Texteditor mit weniger als 1024 Zeilen Code |
| lapce | vim | vim-ähnlicher Code- und Texteditor mit integrierter Kommandozeile |
| pepper | vim | schlanker Code- und Texteditor |
| zed | atom, vim | schneller Multiplayer-Codeeditor mit optionalem vim-Modus von den Entwicklern des Codeeditors Atom |
| zee | emacs | moderner, durch emacs inspirierter Texteditor |
| Markdown | ||
| inlyne | – | GPU-beschleunigter Markdown- und HTML-Viewer |
| mdcat | mdless | cat für Markdown |
| Hex | ||
| heh | dhex | Terminal-Hexeditor, der Daten in Hex und ASCII schreibt |
| hexyl | hexdump | Hexviewer mit Syntaxhighlighting für die Byte-Kategorien |
| teehee | bvi | durch vim, Kakoune und Hiew inspirierter Hexviewer |
| CSV | ||
| csvlens | sc, sc-im | interaktiver CSV-Datei-Viewer |
| qsv | xsv | Werkzeug zum Bearbeiten, Analysieren und Bereinigen von CSV-Dateien, Fork von xsv |
| xsv | xsv, csvkit | Werkzeug zum Bearbeiten, Analysieren und Bereinigen von CSV-Dateien |