4 Punkte von GN⁺ 2024-03-01 | 1 Kommentare | Auf WhatsApp teilen
  • Für Nutzer, die Webartikel direkt im Terminal lesen möchten, stellt James' Coffee Blog seine Blogbeiträge auch im Format von Linux-Handbuchseiten bereit
  • Selbst bei derselben URL wird per HTTP Content Negotiation statt HTML ein roff-Dokument ausgeliefert, wenn der Client Accept: text/roff sendet
  • Die .man-Datei jedes Beitrags wird aus einem Template mit den Abschnitten TITLE, AUTHOR, PUBLISHED, POST und URL erzeugt
  • Im Haupttext wird der Markdown-Originaltext verwendet, damit er lesbarer ist als HTML; in Handbuchseiten sind die Abstände jedoch nicht immer sauber ausgerichtet
  • NGINX erkennt Anfragen nach text/roff und schreibt die URL auf eine .man-Datei um, sodass man sie mit curl speichern und anschließend etwa mit man ./post.page öffnen kann

Blogbeiträge mit man lesen

  • Handbuchseiten unter Linux sind die Standardmethode, um die Nutzung von Befehlen im Terminal nachzuschlagen, und werden üblicherweise mit man <command> geöffnet
  • Zum Beispiel lässt sich die Handbuchseite des Befehls tac so anzeigen
man tac
  • James' Coffee Blog hat einen Ablauf eingerichtet, mit dem sich auch Web-Blogbeiträge auf dieselbe Weise lesen lassen: Von der Beitrags-URL wird eine roff-Version heruntergeladen und mit man geöffnet
  • Ein reales Anfragebeispiel sieht so aus
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page && man ./post.page

Formatauswahl per HTTP Content Negotiation

  • Kern der Implementierung ist HTTP Content Negotiation, bei der der Client dem Server das gewünschte Antwortformat mitteilt
  • Der Header Accept wird verwendet, um den gewünschten Content-Type zu übermitteln
    • Zum Beispiel bedeutet Accept: image/png, dass nach Möglichkeit eine PNG-Datei gesendet werden soll
    • Man kann auch mehrere Content-Types und Prioritäten angeben; hier wird jedoch nur die Anforderung eines bestimmten Formats genutzt
  • Wenn ein Blogbeitrag im Format einer Handbuchseite empfangen werden soll, sendet man den Header Accept: text/roff
  • Der Server erkennt diesen Header und gibt statt HTML eine text/roff-Antwort zurück, die sich mit man öffnen lässt

Erzeugung der .man-Dateien

  • Linux-Handbuchseiten werden in der roff-Syntax geschrieben
  • Die Website wurde so angepasst, dass für jeden Blogbeitrag eine man-Page-Version erzeugt wird
  • Die verwendete Template-Struktur sieht wie folgt aus
.TH jamesg.blog 1 "" "jamesg.blog"
.SH TITLE
...
.SH AUTHOR
James' Coffee Blog (https://jamesg.blog)
.SH PUBLISHED
...
.SH POST
...
.SH URL
...
  • Das Template verwendet den Domainnamen als Header und erstellt fünf Abschnitte
    • TITLE
    • AUTHOR
    • PUBLISHED
    • POST
    • URL
  • Im Haupttext wird der Markdown-Originaltext verwendet
    • In der Handbuchseite passen die Abstände nicht immer sauber
    • Dennoch war er besser lesbar als HTML, und im Vergleich zu reinem Text gingen weniger Informationen über Überschriften und Absatzstruktur verloren

Mit curl abrufen und mit man öffnen

  • Die roff-Version eines Blogbeitrags kann mit folgendem Befehl angefordert werden
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page
  • Das gespeicherte Ergebnis lässt sich wie eine lokale Handbuchseite öffnen
man ./post.page
  • Wenn ein normaler Browser dieselbe Beitrags-URL anfragt, erhält er die HTML-Version
  • Der obige curl-Befehl fordert dagegen für dieselbe URL ausdrücklich die text/roff-Version an

Umschreiben auf .man-Dateien in NGINX

  • Der Server behandelt text/roff-Anfragen separat mit wenigen Zeilen NGINX-Konfiguration
  • In /etc/nginx/nginx.conf wird eine Variable deklariert, die ein Flag setzt, wenn ein bestimmter Content-Type erkannt wird
map $uri $redirect_suffix {
~^/(.*)/$ $1;
default "";
}
map $http_accept $redirect_location {
default "";
"~^text/roff" 1;
}
  • In der Site-Konfigurationsdatei unter /etc/nginx/sites-enabled wird eine Regel hinzugefügt, die Anfragen nach roff-Seiten verarbeitet
server {
...
location / {
if ($redirect_location = 1) {
rewrite ^/(.*)/$ /$1.man last;
}
...
}
}
  • Diese Einstellung entfernt den abschließenden Slash der URL und hängt .man an, wenn der Header Accept: text/roff vorhanden ist
  • Dadurch liest NGINX statt der jeweiligen index.html die entsprechende .man-Datei eines Beitrags
  • So entsteht eine Konfiguration, mit der sich derselbe Blogbeitrag im Webbrowser als HTML und im Terminal als Linux-Handbuchseite lesen lässt

1 Kommentare

 
GN⁺ 2024-03-01
Hacker-News-Kommentare
  • Es wäre cool, ein deb-Repository als eine Art Blog-Abonnement anzubieten
    So könnte man mit apt update alle Artikel abrufen und mit man your-blog den neuesten Beitrag sowie Links zum Gesamtindex aller Beiträge sehen

    • Die Idee selbst ist großartig, aber wenn sich das verbreitet, wirkt die darin angelegte Möglichkeit zur Malware-Verbreitung ziemlich offensichtlich
      Ich glaube, ich hätte Angst, so etwas zu abonnieren
    • Es gibt dafür tatsächlich Vorbilder. Debian bot früher Zugriff auf die inzwischen eingestellte Linux Gazette, und stellt auch heute noch verschiedene Informationspakete bereit, etwa Paketdokumentation, Manpages, Info-Seiten, RFCs, Linux-HOWTOs usw.
      Diese lassen sich lokal mit dem Paket dwww ansehen: „Read all on-line documentation with a WWW browser“
      https://packages.debian.org/bookworm/dwww
      Joerg Jaspert war früher der Paketbetreuer der Linux Gazette: https://people.debian.org/~joerg/ (2002)
      Das war eines der besten Beispiele, die ich bisher dafür gesehen habe, wie Informationsvermittlung und Dokumentation ins Betriebssystem integriert werden können, und es machte insbesondere Man-/Info-Dokumente nützlicher als traditionelle terminalbasierte Oberflächen
      Es gibt auch Debian Planet als Debian-bezogenen Blog, aber soweit ich weiß, wurde es nie als offizielles Debian-Paket bereitgestellt
      Ehrlich gesagt ist RSS für das Abonnieren von Blogs wahrscheinlich die bessere Wahl
    • Ich arbeite gerade daran
      Unter https://github.com/capjamesg/jamesg.blog.deb gibt es Inhalte, mit denen sich mit den folgenden Befehlen eine deb-Datei erzeugen lässt, die nur Manpages enthält
      git clone [https://github.com/capjamesg/jamesg.blog.deb](<https://github.com/capjamesg/jamesg.blog.deb>;)
      cd jamesg.blog.deb
      dpkg-deb --build --root-owner-group jamesg.blog
      sudo dpkg -i jamesg.blog.deb
      Danach sollte eine Ausgabe wie Processing triggers for man-db (2.9.1-1) ... erscheinen; das bedeutet, dass die Manpage für man jamesg.blog verfügbar ist
      Im Moment ist es noch nur ein Platzhalter, aber ich werde es wahrscheinlich morgen fertigstellen
      Vielleicht wird daraus bald ein Blogbeitrag
  • Man kann es auch direkt an man weiterleiten, ohne zu forken oder temporäre Dateien zu verwenden
    curl -sL -H "Accept: text/roff" [https://jamesg.blog/2024/02/28/programming-projects/](<https://jamesg.blog/2024/02/28/programming-projects/>;) | man -l -

    • Davon würde ich abraten. Vor zwei Stunden hat yrro etwas Ähnliches gepostet, und jetzt beginnt schon wieder die Debatte darüber, {curl,wget} in Befehle zu pipen
      Ein Freund lässt einen Freund nicht einen Stream direkt in einen Befehl pipen
      https://news.ycombinator.com/item?id=39554044
  • Nur zur Info: curl -sL -H "Accept: text/roff" [https://jamesg.blog/2024/02/28/programming-projects/](<https://jamesg.blog/2024/02/28/programming-projects/>;) | man -l /dev/stdin funktioniert in meiner Umgebung
    Man muss die roff-Datei also nicht lokal speichern

    • Ich vermute, der Autor des Originalposts hat das absichtlich nicht so gemacht. Aus dem Internet geholte Befehle oder Inhalte direkt in etwas wie bash zu pipen, gilt im Allgemeinen als schlechte Praxis
      Ich persönlich finde das in Ordnung. Wer die sicherheitsrelevanten Folgen versteht, kennt mit hoher Wahrscheinlichkeit auch diese Umwandlungsmethode, daher muss man sie nicht extra erwähnen
      Für Anfänger ist das aber keine gute Empfehlung. Irgendwann kann einen das erwischen. Wenn die eigenen Fähigkeiten wachsen, lernt man solche Möglichkeiten ohnehin kennen, und hoffentlich bis dahin auch ihre Implikationen
      Nicht von mir geschrieben: https://www.seancassidy.me/dont-pipe-to-your-shell.html
    • Leider funktioniert dieser Befehl unter macOS nicht: /usr/bin/man: illegal option -- l
      Ich habe versucht, einen funktionierenden Einzeiler mit Pipe für den Mac zu bauen, bin aber immer wieder auf Fehler gestoßen
      Die man-Implementierung unter macOS hat kein Flag -l. Ich habe die Manpage geprüft
    • Wenn man bash verwendet, kann man statt einer Pipe mit Prozesssubstitution ein paar Zeichen sparen
      man -l <(curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/)
  • Wenn es um URLs geht, die im Terminal etwas Unterhaltsames tun, fällt mir etwas ein, das ich früher auf textfiles.com gesehen habe
    Es zeigt einen kurzen animierten Film mit VT100-Terminalcodes, alles aus einer einzigen URI
    Auf modernen Systemen kann man eine Geschwindigkeitsbegrenzung setzen
    curl --limit-rate 1000 [http://textfiles.com/sf/STARTREK/trek.vt](<http://textfiles.com/sf/STARTREK/trek.vt>;) && reset
    reset ist dabei, weil das Terminal kaputtgehen kann
    Andere terminalbasierte URIs sind curl cheat.sh/tar, das Nutzungsbeispiele für das Programm nach dem / holt, und curl wttr.in/berlin, das Wetterinformationen mit Terminal-Formatierung abruft

    • Wenn man direkt über telnet ASCII-Video erzeugen will: Ich habe vor ein paar Jahren etwas in Go gebaut: https://github.com/bfontaine/RickASCIIRoll
      Eigentlich ist es ziemlich simpel, und der schwierigste Teil ist die Erzeugung der Frames
      Das geht mit ffmpeg+img2txt.py: https://github.com/bfontaine/RickASCIIRoll/tree/master/movie...
    • Vor ein paar Jahren habe ich einen ANSI-Art-Viewer mit Modemgeschwindigkeits-Emulation gebaut
      Es gibt einen alten Mirror von https://16colo.rs/, sodass man den Großteil der jemals veröffentlichten ANSI-Art sehen kann
      Beispiel: curl ansi.hrtk.in/ungenannt_1453.ans
    • Wirklich cool, hat aber auch mein Terminal komplett zerschossen. Hat Spaß gemacht
    • Es gibt auch Star Wars über telnet
      https://itsfoss.com/star-wars-linux/
    • Mit tritty kann man 1200/9600 BPS Übertragungsgeschwindigkeit nachahmen
  • Jetzt fehlt nur noch ein Konverter von Markdown nach roff, aber wie sich herausstellt, gibt es den schon
    https://github.com/postmodern/kramdown-man
    https://rtomayko.github.io/ronn/ronn.1.html
    https://kristaps.bsd.lv/lowdown/

  • Es gibt ein Emacs-Paket, das SICP von Abelson und Sussman im Info-Verzeichnis installiert
    Man muss nur M-x package-install sicp RET eingeben
    Als ich das gesehen habe, dachte ich, dass man mit einem angepassten Feed-Reader vielleicht auch ein ganzes Bücherregal voller Blog-Archive installieren könnte
    Wenn man Info in Emacs liest, kann man auch Lesezeichen verwenden

    • Man muss auch chicken-scheme installieren. Danach als root ausführen
      chicken-install srfi-203
      chicken-install srtfi216
      Die ~/.csirc für SICP sieht so aus
      (import scheme)
      (import (srfi 203))
      (import (srfi 216))
      (define (inc x) (+ x 1))
      (define (dec x) (- x 1))
      Danach kann man wie gewohnt user geiser und geiser für chicken verwenden
    • Zur Einordnung: SICP geht auf Abelson und Sussman zurück
  • Ich könnte die Antwort vermutlich im Internet finden, aber ich wollte HN fragen
    Ich erinnere mich, dass mir in der Highschool auf HP-UX jemand gezeigt hat, wie man mit irgendeiner Tastenkombination zu einem unterstrichenen Wort springt, also zu einem Abschnittsverweis, aber ich weiß absolut nicht mehr, welche Taste das war
    Ich habe auch man(1) und man(7) geprüft, aber nichts gefunden. Vielleicht ist es eine falsche Erinnerung

    • Wenn es man war, muss man bedenken, dass man ohman im Wesentlichen nroff -man /usr/share/man/man1/ohman.1 | $PAGER ist
      Man interagiert also nicht mit man oder nroff, sondern mit dem Pager
      Heute ist less am verbreitetsten, und more ist in der Praxis möglicherweise ebenfalls less, aber früher gab es andere, und HP-UX könnte etwas wie pg verwendet haben
      pg gehörte zur AT&T-Welt, more zur BSD-Welt und less zur GNU-Welt
      Alle drei starten mit / eine RegExp-Suche, sodass man unabhängig von der Unterstreichung danach suchen kann
      less unterstützt auch Tag-Dateien, sodass man mit t zum nächsten Tag springen kann
    • Ich kenne keine spezielle man-Viewer-Funktion, aber vielleicht erinnerst du dich an den CDE-Hilfe-Viewer dthelpview. Der könnte man-Seiten angezeigt haben
    • Das klingt nach texinfo, das man mit dem Befehl info öffnet
      Ironischerweise wurde ein erheblicher Teil der ursprünglichen groff-Dokumentation in texinfo geschrieben: https://lists.gnu.org/archive/html/groff/2005-10/msg00107.ht...
  • Ich weiß nicht, warum dieser kleine Punkt meinen inneren Erbsenzähler so getriggert hat. Vielleicht, weil im Internet jemand ein klein wenig falschlag.
    Vielleicht auch, weil es von Anfang an unnötig Linux-zentriert war oder weil ich etwas anderes erwartet hatte und es am Ende nur eine kurze Demo von NGINX-Content Negotiation war.
    Jedenfalls gibt es ein paar unnötige Dinge, die ich unbedingt sagen will.
    Streng genommen wird kein roff zurückgegeben. Dinge wie .TH sind nicht roff selbst, sondern Teil eines Makropakets zum Schreiben von man-Seiten.
    Ich war enttäuscht, dass es keine Markdown-to-roff-Konvertierung gab. Ich dachte, das wäre der interessante Teil dieses Beitrags, und man hätte dafür wenigstens ein bestehendes Tool verwenden können.
    Ähnlich ist deshalb auch die Textformatierung eigentlich nicht ganz korrekt. roff-Eingaben gehen absichtlich von einer Zeile pro Satz aus, um . am Satzende von . mit anderer Bedeutung zu unterscheiden.
    Außerdem können alle Zeilen, die mit . beginnen, als Befehle interpretiert werden und Probleme verursachen.
    Oder ich bin einfach nur ein grantiger alter Mann.

    • Danke fürs Teilen. Ich wusste nicht genau, wie die Struktur der Beziehung zwischen roff und man aussieht, und habe beim mehrfachen Überarbeiten dieses Beitrags versucht, das korrekt hinzubekommen.
      Andere Tools wie groff und nroff haben es noch verwirrender gemacht.
      Ein Beitrag, der nur erklärt, „was roff/man page/nroff/andere Varianten sind und wie man sie verwendet“, könnte für sich genommen schon ein guter Blogpost sein.
      Eine kurze und klare Erklärung hätte ich auch gut gefunden, und sie wäre vermutlich auch für andere hilfreich.
      Markdown-to-roff hatte ich für v2 vorgesehen. Als ich darüber nachzudenken begann, wie ich einen Parser implementieren könnte, hat mich jemand auf https://github.com/sunaku/md2man hingewiesen, und das scheint dieses Problem zu lösen.
      Ich muss nur noch herausfinden, wie ich das in meine Python-Seite integriere, die auf GitHub Pages läuft, also braucht es noch etwas Feinschliff.
    • Dass es keine Markdown-to-roff-Konvertierung gab, hat mich auch ziemlich überrascht.
      Pandoc kann Markdown sehr einfach in man-page-roff umwandeln.
      Wenn man das in die gegebene Vorlage einsetzt, würde es viel mehr wie eine echte man-Seite aussehen.
  • Der korrekte Medientyp ist laut RFC 4263 text/troff: https://www.rfc-editor.org/rfc/rfc4263.html

  • Coole Idee. Jetzt muss man nur noch die Uhr starten, bis jemand auf „meine Blogposts als spielbares DOOM WAD bereitstellen“ kommt.

    • Einfach zur Liste der wenigen coolen Dinge hinzufügen, bei denen KI tatsächlich helfen kann.