Netzwerkpläne, verschachtelte Abhängigkeiten oder binäre Bäume – mit Graphviz visualisieren Sie auf einfache Weise komplexe Zusammenhänge.
Graphviz-Serie
| Michael Niedermair: “Richtig arrangiert” | LU 01/2014, S. 46 | https://www.linux-community.de/28542 |
| Michael Niedermair: “Dot2tex” | LU 06/2014, S. XXX | https://www.linux-community.de/28543 |
In der Open-Source-Landschaft ist das Programm Graphviz [1] seit Längerem fest verankert (siehe Kasten “Aus den Bell Labs”). Vielfach leistet es unbemerkt im Hintergrund seine Dienste, etwa beim automatischen Erstellen von Abbildungen auf einem Webserver über ein Skript aus einer Datenbank heraus.
Aus den Bell Labs
Die plattformübergreifende Visualisierungssoftware Graphviz stammt ursprünglich von AT&T und den Bell Labs. Die Software existiert seit 1988, steht unter der Eclipse Public License (EPL) [2] und läuft unter den Betriebssystemen Linux, Solaris, Windows und Mac OS X. Für alle Distributionen stehen stabile Pakete bereit.
Die Markdown-Sprache Asciidoc [3] bietet die Möglichkeit, Graphviz-Daten direkt einzubetten – der Compiler übersetzt diese Abschnitte automatisch in entsprechende Grafiken. Für Doxygen [4] und diverse Wiki-Plattformen stehen passende Plugins bereit. Ebenso kommt Graphviz in Puppet [5] zum Einsatz – hier zum Erzeugen von Ressourcen-Graphen.
Beim Verarbeiten gerichteter und ungerichteter Graphen (siehe Kasten “Graphen-Typen”) helfen die Kommandozeilenwerkzeuge dot, neato, fdp, circo und twopi. Sie alle gehören zum Graphviz-Paket und setzen jeweils spezifische Algorithmen aus der Graphentheorie zur Reduktion von Kantenlängen, Separierung von Teilgraphen und Erkennen von Zusammenhangskomponenten [6] um.
Graphen-Typen
Ein Graph besteht aus einer Menge von Knoten und Kanten, wobei eine Kante stets zwei Knoten miteinander verbindet. Es gibt zwei Typen von Graphen: ungerichtet und gerichtet. Bei einem ungerichteten Graphen weisen die Kanten keine Angabe zur Richtung auf. Bei gerichteten Graphen dagegen verfügen alle Kanten über eine solche. Diese gibt die Leserichtung vor, ähnlich einer Einbahnstraße.
Arbeitsablauf
Zunächst erzeugen Sie mit einem Texteditor eine Beschreibung der Elemente und deren Abhängigkeiten untereinander und speichern diese als Textdatei ab. Im nächsten Schritt steht das Übersetzen der Datei mit einem der genannten Tools in ein Ausgabeformat an (GIF, PNG, SVG, PDF, Postscript). Der folgende Befehl erzeugt aus graph.dot eine Abbildung im PNG-Format:
$ dot -Tpng graph.dot -o graph.png
Der Aufbau der Dot-Datei gestaltet sich recht einfach: Zunächst definieren Sie einen Graphen (Listing 1, Zeile 1). Mit dem Schlüsselwort digraph beginnt ein gerichteter Graph, auf Englisch abgekürzt für “directed graph”. Vergessen Sie keinesfalls die öffnende, geschweifte Klammer nach dem Schlüsselwort.
In den darauffolgenden Zeilen spezifizieren Sie die drei Knoten A, B und C mit entsprechenden Attributen zum Gestalten der Knoten und Kanten. Jede Zeile schließen Sie am Ende mit einem Semikolon ab. Als Name eines Knotens verwenden Sie entweder einen Bezeichner, wie er in der Programmiersprache C zulässig ist, oder eine Zahl beziehungsweise eine in Anführungszeichen eingeschlossene Zeichenkette. Leer-, Steuer- und sonstige Satzzeichen respektiert Dot ebenfalls. Mehrzeilige Beschriftungen erzeugen Sie mittels \n für einen Zeilenumbruch. Die Beschriftung erscheint dann mittig.
Der Operator -> erzeugt eine Kante zwischen zwei Punkten in der entsprechenden Richtung. In Zeile 6 schließen Sie die Definition des Graphen mit einer geschweiften Klammer ab. Mit der Vergabe von Attributen steuern Sie das Aussehen des Graphen. Die Attribute geben Sie in eckigen Klammern an. Das ist entweder als allgemeine Anweisung für alle nachfolgenden Elemente möglich oder nur explizit für einzelne Knoten. Letzteres zeigen die Zeilen 2 und 3 in Listing 1.
Listing 1
digraph G {
A -> B [style=dotted];
A -> C [color=red, label="Beschriftung"];
C [shape=box, style=filled, color="0.7 0.7 0.0"];
B -> C;
}
Des Weiteren definiert Zeile 2 eine gepunktete Linie, Zeile 3 stattdessen eine rot gefärbte Linie mit einer Beschriftung der Kante. Dazu verwenden Sie das Attribut label, gefolgt von einem Gleichheitszeichen und dem Inhalt in Anführungszeichen. In Zeile 4 legen Sie für den Knoten C einen eckigen Rahmen fest, dessen Inhalt eine blaue Farbe erhält. Den genauen Farbwert setzen Sie aus den Rot-, Grün- und Blau-Anteilen zusammen. Abbildung 1 zeigt den Quellcode und das Ergebnis im Bildbetrachter.
In der praktischen Arbeit mit dem Rechner eignet sich Graphviz unter anderem dazu, Verzeichnishierarchien zu visualisieren. Ein ungerichteter Graph zeigt in einer Baumstruktur, wo sich möglicherweise unerwünscht Ordner angesammelt haben. Das Programm bietet mit der Option folder für die Ausgabe der Knoten kleine Symbole an, deren Aussehen sich an jenes von Ordnern im Dateimanager anlehnt (Listing 2).
Listing 2
graph {
"/" [shape=folder];
"/boot" [shape=folder];
"/usr" [shape=folder];
"/home" [shape=folder];
"/var" [shape=folder];
"/frank" [shape=folder];
"/peter" [shape=folder];
"/" -- "/boot";
"/" -- "/usr";
"/" -- "/home";
"/" -- "/var";
"/home" -- "/frank";
"/home" -- "/peter";
}
Diese Datei übersetzen Sie analog zum vorher gezeigten Beispiel. Abbildung 2 präsentiert die erzeugte Grafik. Nutzen Sie eines der anderen Werkzeuge, übersetzen diese obige Beschreibung zwar anstandslos, ordnen die Objekte aber kreisförmig an, was der gewohnten Sicht auf ein Dateisystem entgegensteht.

Abbildung 2: Graphviz bringt von Haus aus eine kleine Grafik mit, die sich dazu eignet, um Ordner in einer Verzeichnishierarchie zu symbolisieren.
Mit etwas Geschick ließe sich die Dot-Datei aus einem Skript heraus erzeugen. Auf diese Weise würden Sie sich automatisiert einen Überblick über einen Zweig des Dateisystems verschaffen.
Binärer Baum
Bäume bestehen als Datenstrukturen aus Knoten und Kanten, wobei die Wurzel den oberste Knoten bildet. Binäre Bäume verfügen über zwei Sorten von Knoten – äußere (ohne Nachfolger) und innere (mit genau zwei Nachfolgern). Das vorrangige Einsatzgebiet solcher Binärbäume stellt das Strukturieren und die möglichst effiziente Suche in sortierten Datenmengen dar.
Graphviz nimmt Ihnen beim Umgang mit solchen Graphen Arbeit ab, wenn Sie das Programm richtig nutzen. Listing 3 zeigt, wie Sie einen solchen binären Baum mit der Software erstellen. Nachdem Sie zunächst in Zeile 2 das Aussehen aller Knoten als Rechteck festgelegt haben, definieren Sie in den Zeilen 4 bis 8 die Struktur und die Inhalte der Knoten.
Listing 3
digraph G {
node [shape=record, height=0.1];
knoten0 [label = "<l> | <m> H | <r>"];
knoten1 [label = "<l> | <m> D | <r>"];
knoten2 [label = "<l> | <m> A | <r>"];
knoten3 [label = "<l> | <m> P | <r>"];
knoten4 [label = "<l> | <m> W | <r>"];
knoten0:l -> knoten1:m;
knoten1:l -> knoten2:m;
knoten0:r -> knoten3:m;
knoten1:r -> knoten4:m;
}
Jeder Knoten verfügt über drei Elemente: Einen Verweis auf den nachfolgenden, linken Knoten (benannt mit l), ein mittleres Datenfeld mit Inhalt (benannt mit m) sowie einen Verweis auf den nachfolgenden, rechten Knoten (benannt mit r). Auf die einzelnen Elemente greifen Sie später über deren Namen zu.
In Zeile 10 verknüpfen Sie das linke Element von knoten0 mit dem mittleren Element von knoten1. Die anderen Knoten binden Sie in ähnlicher Weise ein. Das gewünschte Element des jeweiligen Knotens adressieren Sie über den Namen des Elements, durch einen Doppelpunkt separiert vom Namen des Knotens. Damit bestimmen Sie die Position, auf die der Pfeil zeigt. Das nachfolgende Übersetzen erzeugt die gewünschten Kästchen und Linien (Abbildung 3).

Abbildung 3: Die Pfeile für den binären Suchbaum setzt Graphviz automatisch an die richtigen Stellen, wenn Sie die korrekte Syntax verwenden.
Datenstruktur als Hashes
Beim Programmieren kommen häufig Datenstrukturen in Form eines Hashes zum Einsatz, wie etwa assoziative Arrays [7]. Graphviz bildet solche Strukturen ohne komplexe Verrenkungen ab, wenn Sie die richtige Syntax verwenden. So legt die Zeile 2 von Listing 4 fest, dass Sie einen gerichteten Graphen generieren möchten – der Parameter LR bestimmt dabei die Richtung von links nach rechts.
Listing 4
digraph G {
rankdir=LR;
node [shape=record, width=0.1, height=0.1];
knoten0 [label = "<f0> | <f1> | <f2>", height=1.5];
node [width=1.5];
knoten1 [label = "{<n> n14 | 719 | <p>}"];
knoten2 [label = "{<n> k71 | 216 | <p>}"];
knoten3 [label = "{<n> n39 | 771 | <p>}"];
knoten4 [label = "{<n> k56 | 250 | <p>}"];
knoten5 [label = "{<n> a34 | 125 | <p>}"];
knoten0:f0 -> knoten1:n;
knoten1:p -> knoten2:n;
knoten0:f1 -> knoten3:n;
knoten0:f2 -> knoten4:n;
knoten4:p -> knoten5:n;
}
Die Zeilen 5 bis 11 definieren den Inhalt des Knotens als zusätzliche Struktur und greifen später auf die einzelnen Elemente über deren Namen zu. Dabei hat knoten0 (Zeile 5) die drei Elemente f0, f1 und f2, während knoten1 bis knoten5 (Zeile 7 bis 11) jeweils über die beiden Elemente n und p verfügen. Das mittlere Element hat keinen Bezeichner, sondern nur den angegebenen Wert. Das Verlinken der Knoten erfolgt in den Zeilen 13 bis 17, den Rest übernimmt Graphviz (Abbildung 4).

Abbildung 4: Wer sich beim Programmieren die Zusammenhänge von Hash-Tabellen klarmachen möchte, dem hilft Graphviz mit den entsprechenden Funktionen.
Abhängigkeiten
Ein anderes Beispiel stammt direkt aus dem “Motorraum” eines Linux-Systems: Mit dem Programm Debtree [8] erkunden Sie die Abhängigkeiten zwischen Paketen auf einem Debian-basierten System. Dazu erwartet es als Parameter den Namen eines oder mehrerer Pakete – der Einfachheit halber verwenden wir im Beispiel lediglich sqlite3 für die Datenbank SQLite3 [9].
Das Programm liefert das Ergebnis im Dot-Format. Der Aufruf aus Listing 5 erzeugt auf der Kommandozeile die angesprochene Liste und leitet das Ergebnis in die Datei sqlite3.dot um (Abbildung 5). Daraus generieren Sie mit Dot eine Bilddatei (Abbildung 6).
Listing 5
$ debtree sqlite3 > sqlite3.dot $ dot -Tpng sqlite3.dot > sqlite3.png

sqlite3 auf einem Debian-basierten System.” width=”300″ height=”203″ />
Abbildung 5: Die Dot-Beschreibung zu den Abhängigkeiten des Paketssqlite3 auf einem Debian-basierten System.
Abbildung 6: Mit Debtree und Graphviz sehen Sie auf einen Blick, was für einen Rattenschwanz an Programmen eine Installation nach sich zöge.
Pfade hervorheben
Manchmal interessiert in einem Graphen ein bestimmter Pfad, wie etwa die kürzeste Verbindung zwischen zwei Punkten – zum Beispiel beim Planen einer Route in einem Wegenetz. Meist erhalten die Kanten zusätzliche Eigenschaften, wie die Distanz, die Beschaffenheit der Wege (Straßenzustand) oder aktuelle Hindernisse wie Baustellen oder Staus. All diese Kriterien fließen in die Bewertung einer Route mit ein.
Abbildung 7 zeigt ein relativ einfaches Beispiel eines solchen Pfads eines Graphen. Die rote Farbe und die veränderte Breite der Linie erreichen Sie über die Attribute color und penwidth. Um das gleichmäßige Sechseck zu erhalten, rufen Sie beim Übersetzen von Listing 6 das Kommando circo auf – alle anderen Elemente im Aufruf bleiben identisch.
Listing 6
graph {
a -- b -- d -- c -- f[color=red,penwidth=3.0];
b -- c;
d -- e;
e -- f;
a -- d;
}
Dot-Betrachter
Neben dem im Graphviz-Paket mitgelieferten und eher antik wirkenden Programm dotty förderte eine Recherche im Web unter anderem Smyrna [10] und ZGRViewer [11] zutage. Ersteres hat den Status “experimentell”, macht aber nach einem Blick in die Dokumentation einen vielversprechenden Eindruck. ZGRViewer basiert auf Java-Programmcode, der bislang nicht für Debian oder Ubuntu als Paket vorhanden ist.
Fazit
Graphviz versteht sich auf sehr umfangreiche Beschreibungen, die sogar Verläufe in den Farben enthalten dürfen. Fühlen Sie sich mit dem Programm trotz dieses Umfangs ungenügend bedient, hilft ein Blick auf ähnliche Projekte. Dazu gehören GraphML [12], die Graph Exchange Language (GXL) [13] oder die Innereien des Formats Scalable Vector Graphics (SVG). Alle drei definieren Strukturen von Grafiken auf der Basis von XML-Formaten. Für GXL finden sich im Graphviz-Paket passende Konverter von und nach Dot.
Danksagung
Der Autor bedankt sich bei Axel Beckert, Wolfram Eifler und Thomas Winde für deren kritische Anmerkungen und Kommentare im Vorfeld dieses Artikels.
Über den Autor
Frank Hofmann (http://www.efho.de) arbeitet in Berlin im Büro 2.0, einem Open-Source Experten-Netzwerk, als Dienstleister. Der Mitgründer des Schulungsunternehmens Wizards of FOSS koordiniert seit 2008 das Regionaltreffen der LUGs aus der Region Berlin-Brandenburg.
Infos
[1] Graphviz: http://www.graphviz.org
[2] Eclipse Public License (EPL): http://en.wikipedia.org/wiki/Eclipse_Public_License
[3] Asciidoc: http://www.methods.co.nz/asciidoc/
[4] Doxygen: http://www.doxygen.org
[5] Puppet: http://de.wikipedia.org/wiki/Puppet_(Software)
[6] Zusammenhangskomponenten: http://de.wikipedia.org/wiki/Zusammenhang_(Graphentheorie)
[7] Assoziative Datenstrukturen: http://de.wikipedia.org/wiki/Assoziatives_Datenfeld
[8] Debtree: http://collab-maint.alioth.debian.org/debtree/
[9] SQLite: http://www.sqlite.org
[10] Smyrna (PDF), http://www.graphviz.org/pdf/smyrna.pdf
[11] ZGRViewer: http://zvtm.sourceforge.net/zgrviewer.html
[12] GraphML, http://graphml.graphdrawing.org/index.html
[13] Graph Exchange Language (GXL): http://www.gupro.de/GXL/







