Einführung
org-mode
wird als das schweizer Taschenmesser des Editors Emacs beschrieben. Das ist in der Tat eine gute Beschreibung. Mit org-mode
lassen sich Projekte und Aufgaben planen, Bücher verfassen, Musik komponieren, Weblogs führen, Webseiten veröffentlichen, Zeiten erfassen, statistische Berichte dynamisch verfassen, und noch einiges mehr.
Eine der für Softwareentwickler interessanten Möglichkeiten, ist die weitgehend sprachagnostische "Literate Programming" Unterstützung, die ich an einem kurzen, einfachen Beispiel eines Shell-Programms im Folgenden vorstelle.
Das von Donald E. Knuth propagierte literate Programming besteht in erster Linie aus das Programm erklärender Prosa, in die der Programmtext eingebettet wird. Dabei können über Referenzen (Namen von Codeblöcken) an anderer Stelle eingeführte Konzepte eingebunden werden und so die Reihenfolge der Dokumentation vom zu generierenden Code entkoppelt werden.
Das Generieren der Dokumentation wird als "weave" bezeichnet, das Generieren des Code als "tangle".
Der nächste Abschnitt entspricht der aus dem literate programmierten Kleinstwerkzeug erzeugten Dokumentation. Im Abschnitt danach betrachten wir das Endprodukt, das Shellprogramm, sowie den org-mode-Code, der dieses Dokument erzeugt.
Das Beispiel: Prozentual gefülltestes Blockgerät bestimmen
Wir konstruieren hier ein Shell-Programm, das von den aktuell eingebundenen Blockgeräten den Mountpunkt des prozentual gefülltestem ausgibt.
Das Vorgehen ist denkbar simpel: Wir benutzen klassische Unixwerkzeuge in einer Aufrufpipeline.
Das Beispiel ist direkt übernommen, aber übersetzt und deutlich ausgeweitet. Das Original ist in der org-mode
Dokumentation zu finden.
Alle eingehängten Geräte bestimmen
Wir bestimmen erst einmal alle aktuell eingebundenen Geräte. Sicherlich könnten mir dazu mount
nutzen und dann mit einiger Magie über die Ausgabe iterieren. Einfacher ist es jedoch, df
zu bemühen ((mount-info)), da dessen Ausgabe bereits alle Informationen enthält, die wir benötigen.
Beachte die Leerzeile am Ende des Codeblocks: Sie ist nötig, um im generierten ("tangled") Code eine neue Zeile hinter dem Backslash zu erhalten. Ich denke das ist ein Bug in der aktuellen org-mode
Version, der mit padline
Behandlung bei noweb
-Dereferenzierung zu tun hat. Edit: In org-mode Versionen/Releases ab heute (23.01.2012) ist das Leerzeilenproblem bei dieser Art von tangling über Unterbäume behoben; Eric Schulte hat, auf das Problem aufmerksam gemacht, im Handumdrehen eine Lösung eingecheckt.
1: df (mount-info)2:
Die Kopfzeile entfernen
Die Ausgabe von df
beginnt mit einer informativen Kopfzeile, die die einzelnen Spalten der Ausgabe benennt. Wir schneiden sie mit sed
weg ((cut-head)).
3: | sed '1d'(cut-head)4:
Die Ausgabe sortieren
Wir extrahieren die uns interessierenden Informationen – Prozentualer Füllungsgrad und Einhängepunkt – mittels awk
((extract-info)), so dass wir Zeilen der Form "Füllung-in-Prozent Einhängepunkt" erhalten. Das Ergebnis können wir mit sort
numerisch aufsteigend sortieren ((sort)).
5: | awk '{print $5 " " $6}' (extract-info)6: | sort -n (sort)7:
Den Einhängepunkt bestimmen
Da wir in der Pipeline aktuell alle eingehängten Geräte, aufsteigend nach ihrem prozentualem Füllungsgrad sortiert vorliegen haben, gestaltet sich der Rest der Aufgabe recht einfach: Wir extrahieren die letzte Zeile mittels tail -n 1
((trim)) und nutzen ein weiteres Mal awk
, um die Spalte der Zeile zu extrahieren die uns interessiert: Den Einhängepunkt ((final)).
8: | tail -n 1 (trim) 9: | awk '{print $2}' (final)10:
Das generierte Shell-Programm
1: #!/bin/sh2: 3: df 4: | sed '1d'5: | awk '{print $5 " " $6}' 6: | sort -n 7: | tail -n 1 8: | awk '{print $2}'9:
Der org-mode
Code
#+POSTID: 411 #+DATE: [2011-12-09 Fri 11:29] #+OPTIONS: toc:nil num:nil todo:nil pri:nil tags:nil ^:nil TeX:nil #+CATEGORY: Geeks! #+TAGS: Emacs, literate programming, org-mode #+DESCRIPTION: #+TITLE: Ein Beispiel für Literate Programming mit Emacs =org-mode= #+EXPORT_EXCLUDE_TAGS: ignoreExport * Einführung [[http://orgmode.org][=org-mode=]] wird als das schweizer Taschenmesser des Editors Emacs beschrieben. Das ist in der Tat eine gute Beschreibung. Mit =org-mode= lassen sich Projekte und Aufgaben planen, Bücher verfassen, Musik komponieren, Weblogs führen, Webseiten veröffentlichen, Zeiten erfassen, statistische Berichte dynamisch verfassen, und noch einiges mehr. Eine der für Softwareentwickler interessanten Möglichkeiten, ist die weitgehend sprachagnostische "Literate Programming" Unterstützung, die ich an einem kurzen, einfachen Beispiel eines Shell-Programms im Folgenden vorstelle. Das von Donald E. Knuth propagierte literate Programming besteht in erster Linie aus das Programm erklärender Prosa, in die der Programmtext eingebettet wird. Dabei können über Referenzen (Namen von Codeblöcken) an anderer Stelle eingeführte Konzepte eingebunden werden und so die Reihenfolge der Dokumentation vom zu generierenden Code entkoppelt werden. Das Generieren der Dokumentation wird als "weave" bezeichnet, das Generieren des Code als "tangle". Der nächste Abschnitt entspricht der aus dem literate programmierten Kleinstwerkzeug erzeugten Dokumentation. Im Abschnitt danach betrachten wir das Endprodukt, das Shellprogramm, sowie den org-mode-Code, der dieses Dokument erzeugt. * Das Beispiel: Prozentual gefülltestes Blockgerät bestimmen :PROPERTIES: :noweb-ref: fullest-disk :END: Wir konstruieren hier ein Shell-Programm, das von den aktuell eingebundenen Blockgeräten den Mountpunkt des prozentual gefülltestem ausgibt. Das Vorgehen ist denkbar simpel: Wir benutzen klassische Unixwerkzeuge in einer Aufrufpipeline. Das Beispiel ist direkt übernommen, aber übersetzt und deutlich ausgeweitet. Das Original ist in der =org-mode= [[http://orgmode.org/org.html#noweb-ref][Dokumentation zu finden]]. ** Alle eingehängten Geräte bestimmen Wir bestimmen erst einmal alle aktuell eingebundenen Geräte. Sicherlich könnten mir dazu =mount= nutzen und dann mit einiger Magie über die Ausgabe iterieren. Einfacher ist es jedoch, =df= zu bemühen ([[(mount-info)]]), da dessen Ausgabe bereits alle Informationen enthält, die wir benötigen. Beachte die Leerzeile am Ende des Codeblocks: Sie ist nötig, um im generierten ("tangled") Code eine neue Zeile hinter dem Backslash zu erhalten. Ich denke das ist ein Bug in der aktuellen =org-mode= Version, der mit =padline= Behandlung bei =noweb=-Dereferenzierung zu tun hat. #+begin_src sh -n df (mount-info) #+end_src ** Die Kopfzeile entfernen Die Ausgabe von =df= beginnt mit einer informativen Kopfzeile, die die einzelnen Spalten der Ausgabe benennt. Wir schneiden sie mit =sed= weg ([[(cut-head)]]). #+begin_src sh +n | sed '1d' (cut-head) #+end_src ** Die Ausgabe sortieren Wir extrahieren die uns interessierenden Informationen -- Prozentualer Füllungsgrad und Einhängepunkt -- mittels =awk= ([[(extract-info)]]), so dass wir Zeilen der Form "Füllung-in-Prozent Einhängepunkt" erhalten. Das Ergebnis können wir mit =sort= numerisch aufsteigend sortieren ([[(sort)]]). #+begin_src sh +n | awk '{print $5 " " $6}' (extract-info) | sort -n (sort) #+end_src ** Den Einhängepunkt bestimmen Da wir in der Pipeline aktuell alle eingehängten Geräte, aufsteigend nach ihrem prozentualem Füllungsgrad sortiert vorliegen haben, gestaltet sich der Rest der Aufgabe recht einfach: Wir extrahieren die letzte Zeile mittels =tail -n 1= ([[(trim)]]) und nutzen ein weiteres Mal =awk=, um die Spalte der Zeile zu extrahieren die uns interessiert: Den Einhängepunkt ([[(final)]]). #+begin_src sh +n | tail -n 1 (trim) | awk '{print $2}' (final) #+end_src * Das generierte Shell-Programm #+begin_src sh -r -n :tangle no :noweb yes :shebang #!/bin/sh #!/bin/sh <<fullest-disk>> #+end_src * Der =org-mode= Code #+INCLUDE: test_sh_tabgle.org example * Programm erstellen /oder/ tangle :ignoreExport: :PROPERTIES: :comments: no :ID: 579cc2c8-2c45-4e28-acaf-2863986e37eb :END: Dieser Abschnitt wird bei der Generierung der Dokumentation ("weaving") nicht berücksichtigt. Er sorgt aber durch den folgenden Codeblock, bzw. dessen Header, zur Zusammenführung des oben erklärten und geschriebenen Code. - =:tangle yes= sorgt dafür, dass der Block beim Generieren des Code berücksichtigt wird - =:noweb yes= sorgt dafür, dass NoWeb Referenzen aufgelöst werden. Da der =org-mode=-Teilbaum "Prozentual gefülltestes..." in seinem ~PROPERTIES~-Drawer =:noweb-ref: fullest-disk= gesetzt hat, führt das Auflösen der Referenz hier zum Konkatenieren aller Codeblöcke dieses Teilbaumes, und damit zu unserem vollständigen Programm. - =:shebang #!/bin/sh= lässt =org-mode= die generierte Datei mit der angegebenen Zeile beginnen und das executable-Bit setzen. #+begin_src sh -r :tangle yes :noweb tangle :shebang #!/bin/sh <<fullest-disk>> #+end_src #+results: # LocalWords: Kleinstwerkzeug gefülltestes Unixwerkzeuge # LocalWords: Aufrufpipeline