24 Punkte von xguru 2020-12-28 | 3 Kommentare | Auf WhatsApp teilen

Open-Source-Leitfaden, der traditionelle Unix-Prinzipien beibehält und modern aktualisiert

  • CLI-Designphilosophie

→ Menschen zuerst

→ Einfache Bausteine, die zusammenarbeiten

→ Konsistenz zwischen Programmen bewahren

→ Nur so viel sagen wie nötig (weder zu wenig noch zu viel Ausgabe)

→ Leicht auffindbar machen (umfassende Hilfe, Beispiele, Vorschläge für den nächsten auszuführenden Befehl, Vorschläge, was bei Fehlern zu tun ist)

→ Wie ein normales Gespräch

→ Robust sein

→ Mit den Nutzern mitfühlen

→ Chaos: Wenn du Regeln brechen musst, mache Absicht und Zweck klar

  • CLI-Richtlinien

→ Grundlagen

✓ Verwende eine Kommandozeilen-Parsing-Bibliothek: Go(Cobra,cli), Node(oclif), Python (Click,Typer), Ruby(TTY)

✓ Bei Erfolg 0, bei Fehlern einen anderen Code als 0 zurückgeben

✓ Ausgabe an stdout

✓ Logs, Fehler und ähnliche Meldungen an stderr

→ Hilfe

✓ Wenn ohne Optionen ausgeführt wird, Hilfe ausgeben (-h, --help)

✓ Standardmäßig eine knappe Hilfe ausgeben

· Was dieses Programm macht

· Ein oder zwei Aufrufbeispiele

· Erklärung der Flags (wenn es nicht viele sind)

· Zusätzliches `--help` für weitere Erklärungen

✓ Bei -h, --help die vollständige Hilfe ausgeben

✓ Einen Weg bereitstellen, um Feedback/Issues zu erhalten

✓ In der Hilfe einen Link zur Web-Dokumentation anbieten

✓ Mit Beispielen erklären

✓ Wenn es viele Beispiele gibt, diese an anderer Stelle bereitstellen (Cheat Sheet oder Webseite)

✓ Kümmere dich nicht um man-Seiten (werden wenig genutzt und funktionieren unter Windows nicht)

✓ Wenn die Hilfe lang ist, durch einen Pager leiten

✓ Die am häufigsten genutzten Flags und Befehle am Anfang der Hilfe anzeigen

✓ Formatierung in der Hilfe verwenden (Fettdruck)

✓ Wenn der Nutzer etwas falsch gemacht hat und du es erraten kannst, gib eine Empfehlung

✓ Wenn dein Befehl erwartet, etwas per Pipe zu erhalten, aber stdin ein interaktives Terminal ist, zeige die Hilfe an und beende sofort

→ Ausgabe

✓ Human-readable-Ausgabe hat die höchste Priorität

✓ Wenn es die Benutzbarkeit nicht beeinträchtigt, machine-readable Ausgabe anbieten

✓ Wenn human-readable Ausgabe machine-readable unmöglich macht, eine --plain-Option bereitstellen, damit sie mit grep / awk usw. genutzt werden kann

✓ Wenn --json angegeben wird, im JSON-Format ausgeben

✓ Im Erfolgsfall ist keine Ausgabe besser, aber falls nötig, halte sie knapp. Mit der Option -q unnötige Ausgabe unterdrücken können

✓ Wenn sich ein Zustand ändert, sage es dem Nutzer (siehe die Ausgabe von git push)

✓ Den aktuellen Systemzustand leicht erfassbar machen

✓ Befehle empfehlen, die der Nutzer ausführen kann. (So wie git status git add / restore vorschlägt)

✓ Aktionen, die über das Innere des Programms hinausgehen, sollten explizit sein. Zum Beispiel Dateien lesen oder schreiben, die der Nutzer nicht angewiesen hat (Cache), oder Verbindungen zu Remote-Servern herstellen (Dateidownload)

✓ ASCII-Art nutzen, um die Informationsdichte zu erhöhen

✓ Farbe gezielt einsetzen. Nicht missbrauchen

✓ Farbe deaktivieren, wenn kein Terminal vorhanden ist oder der Nutzer es verlangt

✓ Wenn stdout kein interaktives Terminal ist, keine Animationen anzeigen

✓ Symbole/Emojis nur verwenden, wenn sie etwas klarer machen

✓ Gib standardmäßig keine Informationen aus, die nur die Ersteller verstehen können

✓ stderr nicht wie eine Logdatei verwenden (zumindest nicht als Standard. Gib Log-Level wie ERR,WARN nur im ausführlichen Modus aus)

✓ Wenn viel Text ausgegeben wird, ein Paging-Tool wie less verwenden

→ Fehler

✓ Fehler abfangen und Meldungen für Menschen umschreiben

✓ Das Signal-to-noise ratio ist wichtig. Wenn derselbe Fehler mehrfach auftritt, mit einer erklärenden Überschrift gruppiert ausgeben

✓ Berücksichtigen, worauf der Nutzer zuerst schaut

✓ Wenn ein unerwarteter/nicht erklärbarer Fehler auftritt, Debug-/Trace-Informationen bereitstellen und erklären, wie dieser Bug an die Entwickler gemeldet werden kann

✓ Das Senden eines Bug-Reports ohne zusätzlichen Aufwand ermöglichen. (Eine URL erzeugen, die alle Informationen enthält, sodass das Einfügen in den Browser genügt)

→ Argumente & Flags

✓ Argumente: Positionsparameter. Die Reihenfolge ist wichtig. cp bar foo und cp foo bar sind verschieden

✓ Flags: Benannte Parameter. Ein einzelner Buchstabe wie -r oder mehrere Buchstaben wie --recursive. Die Reihenfolge ist im Allgemeinen nicht wichtig.

    Können auch Nutzereingaben enthalten. `--file foo.txt` oder `--file=foo.txt`

✓ Bevorzuge Flags gegenüber Argumenten. Sie erfordern mehr Tipparbeit, sind aber klarer. Viele Argumente erschweren spätere Funktionserweiterungen

✓ Biete sowohl die kurze als auch die lange Form von Flags an. Verwendet man in Skripten die lange Form, braucht es keine weitere Erklärung

✓ Verwende Ein-Buchstaben-Flags nur für häufig genutzte Flags

✓ Für einfache Aktionen kann es auch sinnvoll sein, mehrere Argumente anzunehmen

✓ Wenn zwei oder mehr unterschiedliche Argumente nötig sind, machst du vielleicht etwas falsch

✓ Verwende für Flags standardisierte Namen (wenn es dafür bereits einen Standard gibt)

-a --all , -d --debug , -f --force , -h --help , -o --output , -p --port , -q --quiet , -u --user

✓ Standardmäßig das wählen, was für die meisten Nutzer passend ist

✓ Wenn der Nutzer ein Argument/Flag angibt, das Eingabe erfordert, aber kein Wert übergeben wurde, den Nutzer zur Eingabe auffordern

✓ Biete immer eine Möglichkeit, Werte per Argument/Flag zu übergeben, und verlange nicht zwingend Eingabe per Prompt

✓ Vor gefährlichen Aktionen immer eine Bestätigung verlangen

✓ Wenn Ein- oder Ausgabe eine Datei ist, - unterstützen, um von stdin zu lesen oder nach stdout zu schreiben

$ curl https://example.com/something.tar.gz | tar xvf -

✓ Wenn ein Flag einen zusätzlichen Wert annehmen kann, besondere Wörter wie none erlauben. ssh -F none

✓ Wenn möglich, Argumente, Flags und Subcommands reihenfolgenunabhängig gestalten

✓ Sensible Argumentwerte (wie Passwörter) sollten aus einer Datei eingelesen werden können

→ Interaktivität

✓ Prompts oder interaktive Funktionen nur verwenden, wenn stdin ein interaktives Terminal ist

✓ Wenn --no-input übergeben wird, keinerlei Prompt oder interaktive Funktion verwenden

✓ Beim Eingeben von Passwörtern die Eingabe des Nutzers nicht sichtbar machen

✓ Den Nutzer leicht aussteigen lassen (nicht wie vim). Ctrl-C muss funktionieren. Wenn Ctrl-C wegen der Ausführung von Programmen wie ssh, tmux usw. nicht möglich ist, klar anzeigen, dass es Escape-Sequenzen gibt, die wie bei SSH mit ~ beginnen

→ Subcommands

✓ Komplexe Werkzeuge können durch Subcommands ihre Komplexität verringern

✓ Wenn es zudem mehrere eng verwandte Werkzeuge gibt, können sie bequem unter einem einzigen Befehl gebündelt werden

✓ Zwischen Subcommands konsistent bleiben. Dasselbe Flag soll dieselbe Bedeutung haben und ähnliche Ausgabeformate liefern

✓ Über mehrere Ebenen von Subcommands hinweg konsistente Namen verwenden

✓ Keine verwirrenden oder ähnlich benannten Befehle verwenden. Zum Beispiel update und upgrade

→ Robustheit

✓ Alle Nutzereingaben validieren. Früh prüfen und verständliche Fehler anzeigen

✓ Reaktionsfähigkeit ist wichtiger als reine Geschwindigkeit

✓ Wenn etwas lange dauert, den Fortschritt anzeigen

✓ Wenn möglich, Aufgaben parallel verarbeiten. Aber mit Bedacht.

✓ Timeouts setzen

✓ Idempotent gestalten. (Bei erneutem Ausführen ändert sich das Ergebnis nicht.) Wenn ein Fehler auftritt, sollte man in der Shell mit dem Pfeil nach oben fortfahren und erneut ausführen können

✓ Crash-only gestalten. Der nächste Schritt nach Idempotenz. Wenn nach einer Aufgabe keine Bereinigung nötig ist oder diese bis zum nächsten Lauf aufgeschoben werden kann, kann das Programm bei Fehlern oder Abbruch sofort beendet werden

✓ Menschen werden dein Programm falsch benutzen

→ Zukunftssicherheit

✓ Änderungen möglichst additiv vornehmen. Keine Kompatibilität brechen, indem bestehende Funktionen geändert werden; stattdessen neue Flags hinzufügen

✓ Wenn eine Änderung nicht additiv ist, zuerst warnen

✓ Änderungen an der Ausgabe für Menschen sind meist in Ordnung

✓ Erstelle kein Catch-all-Subcommand, das einen häufig genutzten Subcommand auch ohne explizite Angabe ausführt

✓ Keine beliebigen Abkürzungen für Subcommands zulassen

✓ Keine „Zeitbomben“ einbauen, die irgendwann nicht mehr richtig funktionieren

→ Signale und Steuerzeichen

✓ Wenn der Nutzer Ctrl-C (INT-Signal) eingibt, so schnell wie möglich abbrechen

✓ Wenn der Nutzer während einer lang laufenden Bereinigung Ctrl-C drückt, dies ignorieren. Beim zweiten Drücken darf ein sofortiges Beenden erzwungen werden

 ^CGracefully stopping... (press Ctrl+C again to force)

→ Konfiguration

✓ Der XDG(X Desktop Group)-Spezifikation folgen

✓ Wenn du Einstellungen änderst, die nicht zu deinem Programm gehören, den Nutzer um Bestätigung bitten und klar sagen, was du tust

✓ Konfigurationsparameter in dieser Prioritätsreihenfolge anwenden

Flags > Shell-Umgebungsvariablen > Konfiguration auf Projektebene (`.env`) > Benutzerkonfiguration > Systemkonfiguration

→ Umgebungsvariablen

✓ Umgebungsvariablen sind für Verhaltensweisen gedacht, die sich je nach Ausführungskontext des Befehls ändern

✓ Um maximale Portabilität zu erreichen, sollten Umgebungsvariablen nur Großbuchstaben/Ziffern/Unterstriche enthalten und nicht mit einer Ziffer beginnen

✓ Wenn möglich, für Umgebungsvariablen einzeilige Werte verwenden

✓ Keine weithin gebräuchlichen Namen verwenden

✓ Wenn möglich, allgemeine Umgebungsvariablen prüfen und verwenden

NO_COLOR, DEBUG, EDITOR, HTTP_PROXY, SHELL, TERM, TERMINFO, HOME, TMPDIR, PAGER, LINES ..

✓ Falls nötig, Umgebungsvariablen aus .env laden

✓ Nicht .env als Erweiterung für Konfigurationsdateien verwenden

→ Benennung

✓ Namen sollten einfache, leicht merkbare Wörter sein

✓ Nur Kleinbuchstaben verwenden und - nur wenn unbedingt nötig

✓ Möglichst kurz halten

✓ Leicht auf der Tastatur eintippbar

→ Distribution

✓ Wenn möglich, als einzelne Binärdatei verteilen

✓ Leicht deinstallierbar machen

→ Analytics

✓ Nutzungs- und Crash-Daten des Werkzeugs nicht ohne Zustimmung des Nutzers an dich senden

3 Kommentare

 
jonnung 2021-01-09

Vielen Dank für den guten Inhalt.

 
xguru 2020-12-28

Dank Rust und Go, die sich gut für Single-Binary-Programme eignen, scheint es immer mehr gute Command-Line-Tools zu geben.

Auch das Erstellen wird immer einfacher und leistungsfähiger.

 
xguru 2020-12-28

Beim knappen Übersetzen habe ich selbst auch viel gelernt. Im Nachhinein denke ich allerdings, dass es vielleicht besser gewesen wäre, gleich das gesamte Repo zu übersetzen. ^^;;