Die weltweit größten Shell-Programme
(github.com/oils-for-unix)- Dieses Wiki ist nicht einfach nur eine Sammlung langer Skripte, sondern eine Liste „substanzieller“ Shell-Programme, die von Hand geschrieben wurden und sogar Datenstrukturen und Algorithmen nutzen
- Die Schwelle liegt im Allgemeinen bei mehr als 5K Zeilen; automatisch generierte Skripte oder repetitive Completion-Skripte werden von den Kernbeispielen ausgeschlossen
- Zu den repräsentativen Beispielen zählen ble.sh mit 87K Zeilen, kalua mit etwa 56K SLoC/Zeilen, Relax-and-Recover mit 35K Zeilen, nb mit 26K Zeilen und winetricks mit 22K Zeilen — weit jenseits dessen, was man üblicherweise mit Shell-Skripten verbindet
- Die Liste umfasst in großem Umfang Werkzeuge für den praktischen Einsatz wie interaktive Zeileneditoren, OpenWRT-Add-ons, Bash-Debugger, TLS-Testtools, Kubernetes-Implementierungen, Backup- und Recovery-Tools, Tools zur Zertifikatsausstellung und Ressourcenmonitore
- Die OSH-„Wild“-Tests parsen zwar mehr als eine Million Zeilen Shell, doch der Großteil davon besteht aus kleinen Programmen und repetitiven Paketdefinitionen von Distributionen und wird daher von großen Shell-Programmen unterschieden
Was gilt als „großes Shell-Programm“
- „biggest“ bedeutet hier nicht einfach rohe Zeilenzahl, sondern „substantial“, also inhaltliche Größe und Komplexität
- Berücksichtigt werden grundsätzlich von Hand geschriebene Shell-Skripte
- Große, von autoconf generierte Skripte sind eine Ausnahme
- Automatisch erzeugte Artefakte wie ein 70K-Zeilen-Skript aus coreutils gelten nicht als große Shell-Programme im substanziellen Sinn
- Shell-Programme, die Datenstrukturen und Algorithmen einsetzen, werden besonders hoch gewichtet
- bash-completion ist zwar ausgefeilt, besteht aber aus einer repetitiven Struktur mit relativ einfachen Funktionen für einzelne Befehle auf Unix-Systemen und ist damit eher ein Gegenbeispiel
- Als grobe Untergrenze gelten mehr als 5K Zeilen
- Die größten nicht repetitiven Shell-Programme liegen meist im Bereich von 10K+ Zeilen
- Programme mit mehr als 100K Zeilen wurden bislang nicht bestätigt
Beispiele der größten Shell-Programme
- akinomyoga/ble.sh: insgesamt 87K Zeilen, ohne Kommentare 63K LoC
- Ein in reinem bash geschriebener interaktiver Zeileneditor ähnlich wie fish
- Die Hauptdatei
out/ble.shhat 39K Zeilen, ohne Kommentare 29K LoC; zusammen mit den Moduldateien sind es insgesamt mehr als 80K Zeilen - Enthält viele japanische Kommentare
- Liest mit
bind -xrohe Bytes vom Terminal, dekodiert sie direkt mit mehreren expliziten Zustandsmaschinen und pflegt bzw. aktualisiert einen Drawing Buffer - Enthält auch Timing und „fibers“
- Detaillierte Informationen zum Shell-Parser stehen im Kommentar zu Issue 663; es gilt als eines der ausgefeiltesten Beispiele für den Einsatz von Datenstrukturen in Shell
- Es gibt Versuche, es unter OSH auszuführen, und der Großteil lässt sich parsen
- Der erste Commit hatte 2015 8K Zeilen / 6K LoC; die eigentliche Entwicklung begann 2013
- kalua: Ein OpenWRT-Add-on, geschrieben in POSIX shell mit etwa 56K SLoC/Zeilen
- Relax-and-Recover: Ein Backup- und Recovery-Tool mit 35K Zeilen und 24K LoC
- Der erste git-Commit stammt aus dem März 2009 und hatte damals 4K Zeilen / 3K LoC
- xwmx/nb:
nbselbst umfasst 26K Zeilen und 22K LoC in bash- Rechnet man die bats-Tests in bash mit ein, kommen weitere 91K Zeilen und 61K LoC hinzu
- Der erste Commit stammt von 2014, eine aktive Commit-Historie beginnt Anfang 2016
- vegardit/bash-funk: Eine Bash-Bibliothek mit insgesamt 27K Zeilen und 24K LoC
- Der erste Commit stammt aus dem Mai 2017 und hatte damals 10K Zeilen / 8K LoC
- winetricks: Ein 22K-Zeilen-Shell-Skript, das verschiedene Windows-Programme unter Wine installiert
- drwetter/testssl.sh: Eine einzelne Datei mit 21K Zeilen bash
- Scheint von Hand geschrieben zu sein
- Begann 2006 mit einigen
openssl-Befehlen - Bleibt beim Parsen an Issue #606 hängen
- rkhunter: Ein 21K-Zeilen-Programm in Bourne shell, geschrieben von 2003 bis 2018
- Die offizielle Website ist rkhunter.sourceforge.net
- Simplenetes: Vorgestellt als „Kubernetes in 17K lines of Shell“
- Wird als überraschendes Beispiel markiert, scheint aber inaktiv zu sein
- Verlinkt ist außerdem ein zugehöriger Hacker News Thread
- inxi 2.3.56: Ein als veraltet markiertes 16K-Zeilen-bash-Programm
- Wurde 2008 von
infobashgeforkt infobashhatte damals 889 Zeilen und begann 2005- Ab v2.9 wurde
inxidurch eine Perl-Implementierung ersetzt
- Wurde 2008 von
- bashdb: Ein Bash-Debugger, geschrieben in etwa 14K Zeilen bash
- Als Hintergrund ist Implementing Debuggers verlinkt
- romkatv/powerlevel10k: Im Verzeichnis
internal/liegen 12K Zeilen zsh-Skripte- Dazu kommen weitere 8K Zeilen an Konfiguration und Helper-Skripten
- Der erste Commit stammt von 2014
- dylanaraps/neofetch: Ein in Bash 3.2 geschriebenes 10K-Zeilen-Programm zur Anzeige von Systeminformationen
- Es beherrscht auch interessante bildbezogene Funktionen
- Der erste Commit stammt von 2015
- distrobox: Ein bash-Skript mit mehr als 7K Zeilen, das die Nutzung beliebiger Linux-Distributionen im Terminal ermöglicht
- acme.sh: Ein 8K-Zeilen-Shell-Skript zur Ausstellung und Erneuerung von Zertifikaten
Auffällige Implementierungen trotz kleinerer Größe
- bashforth: Mit etwa 3.800 Zeilen nicht besonders groß, implementiert aber eine echte Programmiersprache
- Enthält viel Whitespace und viele Kommentare
- yoda: Etwa halb so groß wie bashforth, implementiert aber einen vollständigen Interpreter und Compiler
- Eine 20 Jahre jüngere Implementierung desselben Autors mit mehr Funktionen
- Versehen mit dem Kommentar: „What learned you have, unlearn you must!”
Weitere wichtige Shell-Programme
- abcde / A Better CD Encoder: Wird zum CD-Ripping verwendet und hat etwa 5.5K LoC
- thc-segfault: Ein pubnix-Server mit 3.3K LoC, größtenteils in Bash geschrieben
- ffmpeg/configure: Das von Hand geschriebene Configure-Skript von FFmpeg mit 8.4K LoC
- ffhevc: Ein vollständig von Hand geschriebener Bash-Wrapper zum Kodieren von HEVC-Video mit FFmpeg und libx265 mit 4K LoC
- ffx264: Ein vollständig von Hand geschriebener Bash-Wrapper zum Kodieren von H.264/AVC-Video mit FFmpeg und libx264 mit 3.9K LoC
- h264enc: Ein vollständig von Hand geschriebener Bash-Wrapper zum Kodieren von H.264/AVC-Video mit MEncoder mit 9.2K LoC
- bashtop: Ein Ressourcenmonitor mit 5.3K LoC
- halcyon: Ein Installationssystem für Haskell-Apps mit 6.6K LoC
- Von Hand geschriebener Code mit Augenmerk auf bash-Semantik und Fehlerprüfung, in einem einzigartigen, von funktionaler Programmierung inspirierten Stil
- wordshell: Rund 7K Zeilen Code zur Verwaltung mehrerer WordPress-Seiten auf der Kommandozeile
- BaCon: Rund 10K Zeilen; wandelt in BASIC geschriebene Programme in C um
- Es gibt sowohl eine BASIC-Implementierung als auch eine Shell-Skript-Implementierung
- FireHOL: Das Hauptskript hat 9K Zeilen, das FireQOS-Tool weitere 3K Zeilen
- Eine Sprache und ein ausführbares Programm, das aus menschenlesbarer Konfiguration eine sichere, zustandsbehaftete Firewall erzeugt
- gxadmin: 11K LoC, eine Sammlung aus templated SQL queries und Utilities zur Datenverarbeitung für die Verwaltung der wissenschaftlichen Workflow-Engine Galaxy
- mulle-bashfunctions: Eine Funktionsbibliothek für bash/zsh mit rund 6K Zeilen
- Wird in
mulle-sdeverwendet, undmulle-sdeist wiederum ein weiteres 100K-Zeilen-Shell-Skript
- Wird in
- x11docker: 11.6K Zeilen zum Ausführen von GUI-Anwendungen in docker- oder podman-Containern
Shell-ähnliche Sprachen und DSLs
- modernish: Ein in Shell geschriebener portabler Shell-Dialekt
- bats: Eine DSL zum Schreiben von Tests, die bash-Code erzeugt
- bashible: Eine in bash geschriebene Ansible-ähnliche DSL
- Verlinkt sind dazu passende comments
- clash: Ein objektorientiertes Framework, kompatibel mit allen modernen POSIX-Shells
- bash Infinity: Eine Standardbibliothek und ein Boilerplate-Framework für bash
Kleinere Programme und das zugehörige Ökosystem
- Alpine-, Aboriginal- und Debian-Skripte sind in einem separaten blog post verlinkt
- Completion-Skripte sind groß, aber oft repetitiv
- Das Zsh-Completion-Skript _git umfasst 8.3K Zeilen Code
- Auch
git-completion.bashund Docker-Completion werden als Beispiele genannt
- dyne/Tomb: Ein zsh-Skript mit etwa 3.500 Zeilen
- Basalt: Ein vollständig in Bash geschriebener full-featured Package Manager
- Umfasst nur einige tausend Zeilen, besitzt aber ein reiches Ökosystem mit mehr als 15 Apps und Bibliotheken
- bash-core: Eine Bibliothek, die die Builtins
trapundshopterweitert und Stacktraces sowie essenzielle Komfortfunktionen ergänzt - bash-object: Eine Bibliothek für beliebig verschachtelte Datenstrukturen in reinem Bash; die Tests umfassen fast 200 Fälle
- bash-json: Eine Bibliothek zum Parsen und Ausgeben von JSON in reinem Bash
- tablespoon/fun/cli-clock: Eine mehrzeilige Zeichen-Uhr, geschrieben in bash
- json.bash / jb: Ein Kommandozeilen-Tool und eine bash-Bibliothek zum Erzeugen von JSON
- Rund 1.700 Zeilen, dazu etwa 3.000 Zeilen Tests
OSH-Tests und Vorsicht bei der Nutzung von Shell
- OSH "Wild" Tests parsen mehr als eine Million Zeilen Shell
- Der Großteil davon besteht aus kleinen Programmen und repetitiven Paketdefinitionen von Distributionen wie Alpine
PKGBUILDoder Gentooebuild
- Der Großteil davon besteht aus kleinen Programmen und repetitiven Paketdefinitionen von Distributionen wie Alpine
- Shell Programs That Run Under OSH verlinkt eine Liste von Shell-Programmen, die unter OSH laufen
- shell script are dangerous warnt davor, dass Shell ein Programm ist, das das Innenleben des Systems interaktiv oder nicht interaktiv behandelt, sehr viele Funktionen besitzt, sehr gefährlich ist und nicht zum Bau von Anwendungen gedacht war
1 Kommentare
Meinungen auf Hacker News
Beim Nachforschen stellte sich heraus, dass das OMS ein riesiges Bündel von Shell-Skripten war, das auf einem AIX-Server lief, sich über mehr als zehn Jahre entwickelt hatte und dann vernachlässigt worden war. Der Code umfasste über 50.000 Zeilen; Bestellungen, Zahlungen und andere Informationen wurden per FTP zwischen Servern verschoben und anschließend mit komplexem sed/awk geparst, und auch der Lagerbestand wurde in Textdateien nachverfolgt und per FTP übertragen.
Damals schien Perl der praktikabelste Weg zu sein, dieses Chaos zu migrieren, und ich begann mit den einfachsten Teilen, ersetzte sie durch kleine Perl-Module und refaktorisierte sie schrittweise innerhalb einer größeren Perl-Anwendung. In drei Monaten reduzierte ich das Ganze auf etwa 5.000 Zeilen Perl, und die Ausfälle des ursprünglichen Systems verschwanden fast vollständig, während es 10- bis 100-mal schneller wurde. Es war schrecklich, aber bis heute eine der befriedigendsten Arbeiten, die ich je gemacht habe.
Ich frage mich, ob der ursprüngliche Code komplett gelesen und tief verstanden wurde, um das Verhalten exakt nachzubilden, oder ob große Teile weggeworfen und nach der eigenen Vorstellung von „so sollte es funktionieren“ neu geschrieben wurden. Ich frage mich auch, ob es viel schnell ersetzbaren Boilerplate-Code gab.
Das erste wirklich große Skript, das ich schrieb, war ein etwa 7.000 Zeilen langes Installationsprogramm für Enrust CA und Directory, das auf so ziemlich jedem Unix der damaligen Zeit laufen musste. So war es anfangs nicht geplant, aber es wuchs mit den Kundenanforderungen.
Die Installation selbst war nicht besonders kompliziert, Upgrades dagegen schon etwas, und damals unterschieden sich auf jedem Unix alle Utilities leicht. Ein großer Teil des Skripts bestand aus Code, der diese Unterschiede erkannte und verwaltete, außerdem Fehlererkennung, Wiederherstellung, Rollback sowie sehr primitive Paket- und Abhängigkeitsverwaltung.
DECs Unix, also die Nicht-Ultrix-Variante, war am undurchschaubarsten. Es dauerte Tage, bis ich begriff, dass alle Kommandozeilen-Utilities ihre Ausgabe auf die Breite des Terminalfensters abschneiden, und selbst nach 30 Jahren erinnere ich mich noch daran.
HP-UX hatte in jedem Release breaking changes, und wenn ich mich richtig erinnere, unterstützten wir Versionen von 6.5 bis 11. An Ultrix, Novell, NeXT und Sequent erinnere ich mich kaum noch. Bei AIX weiß ich noch, dass es seltsam war, aber nicht mehr warum. Auch Suns drei/vier Betriebssysteme hatten Unterschiede, aber die Handbücher waren hervorragend, die besten überhaupt.
wcüber das Haupt-Binary und die Hilfsbibliotheken eines Projekts laufen ließ, waren es bis dahin 6.224 Zeilen.Es war ein Skript zur Verwaltung einer linear garantierten Pipeline aus einer Container-Gruppe mit Eingabeprotokoll-Adapter, einem oder mehreren Filtern und Ausgabeprotokoll-Adapter. Ziel war, es für Leute nutzbar zu machen, die keine Container- oder Protokoll-Experten sind, aber wissen, wie Dateien beim Durchlaufen der Pipeline gefiltert und transformiert werden sollen.
Das Top-Level-Binary ist wie
git [ git options ] < git action> [action options]odersystemctlmit Unterfunktionen strukturiert. Es gibt sogar einen Unterbefehl, der neue Unterbefehle hinzufügt, die nötigen Bibliotheken anlegt und Funktionsdefinitionen aus Templates vorbefüllt. Die Templates enthalten Funktionen für kurze und lange Nutzungshinweise, sodasscbap -hodercbap pipeline -hhilfreiche Anleitungen ausgeben.Es gibt Unterbefehle zum Manipulieren von Basis-Images, Komponenten und Pipelines. Ein erheblicher Teil des Codes dient Tests, die prüfen, ob Komponenten- und Pipeline-Definitionen korrekt geschrieben sind. Pipelines verwenden ein Format, das fast TOML ist, daher gibt es Code zum Parsen von TOML und zum Umwandeln von Sektionen in Arrays; Komponenten sind einfache
key=value-Dateien, daher gibt es Code zum Extrahieren der linken und rechten Seite und zur Schema-Validierung.Weil Pipeline-Komponenten Eigenschaften gemeinsam nutzen können, gibt es auch Code, der gemeinsame Eigenschaften in
var- undetc-Dateien findet und Komponentenattribute setzt. Außerdem gibt es viele Funktionen zum Manipulieren von Benutzern, Gruppen, Verzeichnissen und FIFOs gemäß den Sicherheitsanforderungen. Beim Einrichten einer Pipeline werden Benutzer, Gruppen, SELinux-Typen und MCS-Kategorien erstellt und angewendet und dann auf Service-Dateien gemappt, die die Komponenten starten; deshalb gibt es auch viel systemd-Manipulation.Die größte Gruppe von Aufrufen sind vermutlich Funktionen zum Abrufen und Setzen von Komponenteneigenschaften, tatsächlich also Containereigenschaften. Für jede Eigenschaft gibt es eine Getter-Funktion, eine Validierungsfunktion und eine Inline-Version innerhalb der Pipeline, um datengetriebene Containerdefinitionen so flexibel wie möglich zu machen.
Es gibt auch Code, der stark auf Bash-Referenzen setzt, um Variablen aus Dateien, Umgebungsvariablen und der Kommandozeile zu setzen, was schnelle Tests ermöglicht. Es werden außerdem vier Nutzerebenen unterstützt: Maintainer, die den Code selbst bearbeiten; Entwickler, die Komponentendefinitionen entwickeln; Integratoren, die aus Komponenten Pipelines bauen; und Operatoren, die Pipelines installieren. Das System kann sich selbst kopieren und paketieren, um es an Nutzer der jeweiligen Ebene auszugeben.
Da das Zielsystem jedes beliebige Linux sein kann, wird mit
makeselfpaketiert und entpackt. Wenn beispielsweise ein Integrator eine Pipeline-Definition erstellt, wird einemakeself-Datei erzeugt; beim Ausführen auf dem Zielsystem legt sie alle Benutzer, Gruppen, Verzeichnisse und FIFOs, also die IPC zwischen Komponenten, an, wendet DAC/MAC an, erstellt systemd-Dateien, kopiert Images zu den jeweiligen Benutzern und startet anschließend die Pipeline. Mit einer Löschoption kann all das auch wieder rückgängig gemacht werden.Ein Teil seccomp ist ebenfalls enthalten, aber das liegt vorerst auf Eis, weil ein Gleichgewicht zwischen Allowlist und Blocklist gefunden werden muss. ShellCheck nutze ich wirklich gründlich.
Man kann nicht erwarten, dass
bc/dcüberall vorhanden sind, und einige meiner Maschinen hatten so alte Bash-Versionen, dass auch die Unterstützung für assoziative Arrays sehr eingeschränkt war. Als Kompromiss wählte ich AWK als Ziel, denn AWK ist als Allzwecksprache deutlich angenehmer als die meisten Shells und in jeder POSIX-Umgebung verfügbar: https://beyondloom.com/blog/lila.htmlbc/dcnicht in jeder Umgebung voraussetzen kann.Ich war ziemlich überrascht, dass in der Ubuntu-Installation von WSL2 offenbar
bc/dcfehlten. Für Gleitkomma-Berechnungen verwende ich zwar AWK, aber nur indem ich es als externen Prozess aufrufe.Aus der Perspektive von jemandem, der im Lauf seiner Karriere mehrfach große Perl-Programme geschrieben und gewartet hat: Es gibt Gründe, warum Leute das so machen.
Sprachen wie Java oder Python passen gut, wenn Schnittstellen und Formate definiert sind und es kaum Interaktion mit dem OS gibt. Wenn man JSON/XML/YAML verwendet oder mit Datenbanken und anderen Programmen über HTTP(S) kommuniziert, ist das der Idealfall, in dem solche Sprachen glänzen.
Sobald man aber große Mengen Text und OS-Interaktion verarbeitet, werden Java und Python sehr schmerzhaft. Umgekehrt merkt man, dass Shell/Perl für solche Aufgaben viel angenehmer sind.
Fast alle Automatisierungsaufgaben, chaotische und nicht standardisierte Schnittstellen, Text-/Logdateien sowie unstrukturierte oder nicht ausreichend strukturierte Datenformate fallen in diese Kategorie. Nimmt man dazu Perls Abwärtskompatibilität, die breite Installationsbasis und die Performance, gibt es für solche Aufgaben praktisch keine Alternative zu Perl.
Ich sehe schon lange einen der Hauptgründe dafür, dass große Unternehmen heute so viel manuelle Arbeit haben und Tausende Leute für Kleinigkeiten beschäftigen, die sich automatisieren ließen, darin, dass Perl weniger genutzt wird. Wenn man versucht, große Automatisierungsaufgaben in Python oder Java zu bauen, gibt man wegen der Geschwätzigkeit des zu schreibenden und zu wartenden Codes und des Gesamtumfangs schnell wieder auf.
Deshalb braucht man jetzt vermutlich zusätzlich teure Vollzeitkräfte. Wenn die Endnutzer Windows verwenden, haben sie bereits eine Perl-ähnliche Option auf dem Desktop: PowerShell, die eine ähnliche Rolle wie Perl erfüllt.
Bash+grep führt einen leicht in die Richtung, für jede Textzeile einen neuen Prozess zu starten. Wenn man effizient sein will, muss man die Arbeit minimieren, und dafür braucht man Batch-Verarbeitung und Deduplizierung. Das bedeutet, dass man Daten zustandsbehaftet verarbeiten und dabei den Deduplizierungskontext verfolgen muss; in einer richtigen Programmiersprache ist das einfacher.
Bash+grep eignet sich gut für zustandslose Textverarbeitung, wodurch leicht viel doppelte Arbeit entsteht. Eine weitere Möglichkeit, Arbeit zu reduzieren, ist präzises Filtern; das lässt sich in einer richtigen Sprache imperativ sauberer ausdrücken. grep und reguläre Ausdrücke passen dafür überhaupt nicht.
Bei zeilenbasierten Formaten fügt git Escapes ein, um alles Mögliche akzeptieren zu können; die Unterstützung ist aber nicht konsistent, und mit der Option
-zkann man ein nullterminiertes String-Format anfordern und das deaktivieren. Bash scheint keine Möglichkeit zu haben, damit umzugehen; in einer ausreichend low-level Sprache lässt sich das natürlich handhaben. Außerdem ist inkrementelles Streaming möglich, ohne für jede Textzeile einen neuen Prozess zu starten.Als Bonus kann man für alle Aufgaben eine einzige Codebasis verwenden, egal ob zwischendurch HTTP oder etwas anderes im Spiel ist.
Ein paar Links zum Kontext: „Erfindet ihr Perl neu?“: https://www.oilshell.org/blog/2021/01/why-a-new-shell.html#a...
„Die Unix-Shell sollte nicht wie der Big Bang von Perl 6/Raku evolvieren, sondern wie Perl 5, mit kompatiblen Upgrade-Optionen“: https://www.oilshell.org/blog/2020/07/blog-roadmap.html#the-...
YSH-Tour: https://www.oilshell.org/release/latest/doc/ysh-tour.html
Das wirkt vielleicht nicht wie ein großer Vorteil, bis man in einer Umgebung arbeitet, in der man nichts aus dem Internet installieren kann oder überhaupt keinen Internetzugang hat.
Das Kernproblem beim Schreiben großer Programme als Bash-Skripte liegt darin, dass die Shell-Skriptsprache ursprünglich nicht für Komplexität entworfen wurde.
Sie ist hervorragend darin, kleine Befehle zu orchestrieren und vorhandene Tools schnell und explorativ zusammenzustecken. Aber sobald Bash über ein paar Hundert Zeilen hinauswächst, treten nacheinander Grenzen zutage, die langfristige Wartung und Erweiterbarkeit mühsam machen.
Zuerst ist Lesbarkeit ein Problem. Die Bash-Syntax kann mit zunehmender Größe wirklich undurchsichtig werden. Die Regeln für Variablenscopes sind subtil, Fehlerbehandlung ist primitiv, und String-Verarbeitung wird schnell unordentlich. Am Ende verschwenden Maintainer Zeit damit, zu entschlüsseln, was passiert, und Änderungen lassen sich nur schwer mit Vertrauen vornehmen.
Als Nächstes fehlt es an robustem Tooling. Reifere Sprachen haben statische Analysewerkzeuge, Linter und Debugger, die häufige Fehler früh abfangen. In Bash gibt es diese Dinge nicht oder nur sehr eingeschränkt. Ohne solche Sicherheitsnetze sind große Bash-Programme anfälliger für stille Fehler, Regressionen und subtile Bugs.
Auch Tests sind ein Problem. Bash-Skripte lassen sich zwar testen, aber der Prozess ist meist umständlicher und wird bei komplexer Logik oder Datenstrukturen noch schwieriger. Wenn man Grenzfälle wie Leerzeichen in Dateinamen oder unerwartete Umgebungsbedingungen behandelt, entsteht schnell viel defensiver Code, dessen Validierung schmerzhaft ist.
Schließlich ist das Ökosystem selbst nicht für Bash-Entwicklung im großen Maßstab gemacht. Man verliert Modularisierung, Paketverwaltung, standardisierte Dependency-Behandlung und moderne Entwicklungsmuster, wie sie Python oder Go bieten. Mit der Zeit summieren sich diese Defizite und bremsen aus.
Für einmalige Aufgaben oder einfache Automatisierung ist Bash in Ordnung. Genau darin ist Bash gut. Wenn man aber etwas Großes bauen will, ist es in der Regel besser, eine Sprache zu verwenden, die für das Erstellen und Warten komplexer Anwendungen entworfen wurde; selbst wenn die anfängliche Lernkurve oder Einrichtung etwas höher ist, spart das langfristig Zeit.
Mit ShellCheck als Linter lassen sich viele gängige Fallstricke finden. Bash/Shells haben wirklich viele Fallen und unerwartete Verhaltensweisen, über die auch erfahrene Bash-Autoren stolpern können.
Allerdings nimmt Bash/die Shell in der Sprachhierarchie eine besondere Stellung ein. Sie ist fast überall vorhanden und wird wahrscheinlich auch in 30 Jahren noch existieren. Wenn man ein Programm will, das fast überall läuft und auch in 30 Jahren noch laufen wird, ist Shell/Bash eine gute Wahl.
Es ist nicht einmal ein uraltes Skript, aber ich weiß nicht, warum man Bash gewählt hat. Das Skript funktioniert, aber es fühlt sich an, als würde etwas kaputtgehen, wenn man den Code nur falsch anschaut.
-xein Argument zum Debuggen und Tracing.Das größte manuell gepflegte Shell-Programm, das ich früher regelmäßig verwendet habe, war vermutlich abcde (A Better CD Encoder), mit etwa 5.500 Zeilen.
https://abcde.einval.com
https://git.einval.com/cgi-bin/gitweb.cgi?p=abcde.git;a=blob...
Viele dieser Programme sind echte Perlen. Das rkhunter-Skript zum Beispiel hat ordentlichen Code, bietet noch Raum für Verbesserungen und ist zugleich eine Fundgrube an Informationen.
Ein großer Teil des Codeumfangs solcher Skripte dient dazu sicherzustellen, dass die auf verschiedenen Plattformen benötigten Utilities vorhanden sind und sich bei unterschiedlichen Kommandozeilenoptionen wie erwartet verhalten. Das ist für ernsthafte Shell-Skript-Autoren der schmerzhafteste Punkt, noch schwieriger als Signale und Unterprozesse.
Wäre rkhunter in einer „richtigen“ Programmiersprache geschrieben, wären diese Informationen wohl weniger transparent. Sie wären vielleicht in Datensätzen innerhalb von Datenstrukturen verschwunden und würden abgefragt, oder sie würden über mehrere Funktionen auf verschachtelten Datenstrukturen laufen – oder schlimmer noch über eine Kombination aus Methoden und Klassen. Auch Logs wären womöglich in JSON zerlegt und in eine Datenbank komprimiert worden, auf die man über andere Methoden zugreifen müsste.
Da Shell-Skripte solche komplexen Werkzeuge nicht haben, neigen sie eher dazu, direkt offenzulegen, was passiert. Deshalb dient rkhunter auch als brauchbare Dokumentation zu verschiedenen Exploits und Rootkits, und man muss sich weniger von Datei zu Datei, von Struktur zu Struktur und von Datenbank zu Datenbank durchgraben.
Der FreeBSD Update-Client besteht aus rund 3.600 Zeilen sh-Code.
Im Vergleich zu anderen hier erwähnten Programmen ist das nicht groß, aber die Funktionsmenge eines „Tools, das ein ganzes Betriebssystem aktualisiert“, ist meiner Meinung nach durchaus gewichtig. Der Code zum Erstellen der Updates ist auf mehrere Dateien verteilt und dürfte zusammen noch umfangreicher sein.
poudriereentspricht, gemessen an sh-Code, etwa drei FreeBSD-Update-Clients: https://github.com/freebsd/poudriere/blob/master/src/share/p...Es sind „nur“ 7,1 Tausend Zeilen, aber mein Favorit ist das Skript acme.sh, das zum Ausstellen und Erneuern von Zertifikaten bei Let’s Encrypt verwendet wird.
https://github.com/acmesh-official/acme.sh/blob/master/acme....
Manchmal ist die Shell das Einzige, dessen Verfügbarkeit man garantieren kann, und Portabilität ist zwingend erforderlich.
Aber im Allgemeinen sollte man bei einer riesigen Shell-Anwendung vielleicht seine Lebensentscheidungen noch einmal überdenken.
Außerdem kann man mit der Shell allein normalerweise nicht viel machen und braucht Befehle wie
find,grep,sed,cat,head,tailundcut. Auch diese Befehle haben jeweils ihre eigenen Portabilitätsprobleme.BusyBox als Ziel zu nehmen, ist vielleicht die beste Option, aber sobald man ein normales Linux-System verlässt, wird das Schreiben portabler Bourne-Shell-Skripte schwierig oder nahezu unmöglich.
Wenn man nur einen C-Compiler hat, kann man die Shell hinter sich lassen und C-Programme schreiben, die von Shell-Skripten kombiniert werden, oder eine bessere Skriptsprache wie Lua installieren. Dass man heute zwingend ausschließlich die Shell verwenden muss, wirkt ziemlich wie ein Nischenfall.