Die Z-Shell (zsh) – Eine mächtige Alternative zur Bash

1. Februar 2010 von zimon

Die Standardshell auf den meisten Linux-Systemen ist die Bash. Sie ist schon sehr mächtig und erlaubt es viele Aufgaben zu automatisieren. Dieser Artikel soll sich einer sehr interessanten weil noch mächtigeren Shell widmen, der Z-Shell kurz zsh.

Die zsh lässt sich auf quasi allen Linuxsystemen über die Paketverwaltung installieren. Unter Ubuntu z.B. mit dem Befehl

sudo apt-get install zsh

Danach kann man sie mit dem Aufruf von zsh ausführen. Um sie zur Standardshell zu machen genügt der Befehl

chsh -s /usr/bin/zsh username

Wobei username durch den entsprechenden Usernamen ersetzt werden muss und der Pfad zu zsh stimmen muss. Auf manchen Systemen liegt zsh in /bin. Wo zsh liegt kann man mit dem folgenden Befehl heraus finden:

which zsh

Die zsh bedient sich Elementen der Bash, der Korn-Shell (ksh) und der TENEX-C-Shell (tcsh – eine erweiterte C-Shell). Sie ist sehr gut konfigurierbar und fast jedes Verhalten der Bash kann nachgeahmt werden so dass der Umstieg sehr leicht fällt. Bestehende Bash-Scripte können natürlich weiterhin genutzt werden, wenn der Shebang entsprechend gesetzt ist. Also in der ersten Zeile folgendes steht:

#!/bin/bash

Im Folgenden werde ich einige interessante Vorteile der zsh kurz beschreiben. Zu einzelnen Themen werde wahrscheinlich in anderen Artikel ausführlicher eingehen. Dies sollen also nur Beispiele sein, die die Mächtigkeit demonstrieren sollen. Da die zsh so viele Möglichkeiten bietet ist es nicht möglich hier alles im Detail zu erläutern. Optionen werden mit dem Befehl setopt gesetzt. Dies kann man auch so in die Konfigurationsdatei ~/.zshrc schreiben.

Autokorrektur
Die zsh beherrscht einen Mechanismus, der kleine Tippfehler automatisch korrigiert. Hat man in einem Ordner eine Datei testfile.txt und möchte diese kopieren nach file2.txt, so führt man in der Regel folgenden Befehl aus:

cp testfile.txt file2.txt

wie von der Bash gewohnt nutzt man dafür die Autovervollständigung mittels TAB. Bei einem Verschreiber wird dieser in der zsh automatisch korrigiert. Beispiel:

cp tsetf<TAB>

wird automatisch korrigiert und zu

cp testfile.txt

vervollständigt.

Die Autokorrektur aktiviert man mit der Option correct

Globale Aliase
Neben den normalen Aliasen für Kommandos, wie man sie aus der Bash kennt, gibt es in der zsh noch globale Aliase, die überall im Befehl genutzt werden können, nicht nur am Anfang. Zwei Beispiele verdeutlichen dies:

alias -g G='| grep'
alias -g L='| less'

erzeugt die globalen Aliase G und L die man hinter alle möglichen Befehle schreiben kann um die Ausgabe des entsprechenden Befehls in grep oder less zu pipen. Z.B.

ls G txt

entspricht dem Befehl

ls | grep txt

oder

ps -e L

entspricht

ps -e | less

Natürlich ist auch folgendes Möglich:

ls G txt L

was folgendem Befehl entspricht:

ls | grep txt | less

Suffix Aliase
Mit Suffixaliasen kann man Programme festlegen, mit denen bestimmte Dateitypen (anhand ihrer Endung) geöffnet werden sollen. Man braucht dann nur noch den Namen der Datei eingeben und sie wird mit dem entsprechenden Programm geöffnet. Beispiel:

alias -s pdf=evince

legt einen Alias für pdf Dateien an. Sie sollen mit evince geöffnet werden. Danach reicht die Eingabe von

dokument.pdf

und die Datei dokument.pdf wird mit evince geöffnet.

Aliase für Verzeichnisse: Hashes
Mit sogenannten Hashes lassen sich Aliase für beliebige Verzeichnisse anlegen. Auf diese kann dann sehr einfach zugegriffen werden. Mit dem Befehl

hash -d perl=~/developement/scripting/perl

legt man einen Hash namens perl für das Verzeichnis ~/developement/scripting/perl an. Auf dieses Verzeichnis kann man jetzt mit ~perl zugreifen. Auch innerhalb eines Befehls wie folgendes Beispiel zeigt:

cp testscript.pl ~perl

Dieser Befehl kopiert die Datei testscript.pl in das Verzeichnis ~/developement/scripting/perl.

Auto-CD
Mit der Option autocd kann man zum Wechseln in ein Verzeichnis einfach den Namen eines Verzeichnisses eingeben und das Kommando cd davor einfach weglassen. Wenn es keinen Befehl gibt, der so heißt wie das Verzeichnis, so wird in das Verzeichnis gewechselt.

Kurze for-Schleifen
Statt einem

for i in *.eps; do epstopdf $i; done

um alle eps-Dateien in einem Verzeichnis in pdf-Dateien umzuwandeln genügt unter zsh die kürzere Form

for i (*.eps) epstopdf $i

Globale History
Die History, die man mit der Pfeil-auf Taste durchgehen kann, kann man in der zsh so einstellen, dass sie in jeder zsh-Instanz gleich ist. Das heißt man hat nicht mehr für jedes Fenster seine eigene History sondern kann mit der Pfeil-auf Taste auch Befehle raussuchen, die in anderen Fenstern eingegeben wurden. Die History wird jedoch nur nach jedem Befehl geupdated, das heißt man muss ggf. einmal Return drücken, damit die Befehle aus einem anderen Fenster verfügbar sind.

Dieses Verhalten kann mit der Option share_history aktiviert werden.

Directory Stack
Wie Bash, hat die zsh auch einen Directory stack. Mit dem Befehl pushd kann man das aktuelle Verzeichnis auf den Stack legen und mit popd das jeweils letzte Verzeichnis vom Stack nehmen und dorthin springen. Mit der Option auto_pushd legt die zsh automatisch jedes Verzeichnis auf den Directorystack, aus welchem man in ein anderes Verzeichnis wechselt. So hat man immer eine History der Verzeichnisse in denen man war und kann mit popd sehr einfach wieder in Verzeichnisse welchseln, die man vorher besucht hatte.

Globbing
Die Nutzung von Wildcards – unter zsh globbing genannt – ist bei der Z-Shell wesentlich mächtiger als in der Bash. Zunächst gibt es rekursives Globbing (gibts in der Bash in neueren Versionen wohl auch). So kann man mit dem Befehl

ls **/*.html

alle HTML-Dateien im aktuellen Verzeichnis und allen Unterverzeichnissen auflisten lassen.

Es gibt jedoch auch noch Qualifier um Dateien mit bestimmten Eigenschaften auszuwählen. Diese schreibt man in runde Klammern. So kann man mit . normale Dateien, mit / Verzeichnisse und mit @ Symbolische Links matchen. Praktisch ist das z.B. wenn man allen Verzeichnissen ab einem bestimmten Verzeichnis die Rechte 755 und allen Dateien 644 vergeben möchte. Dies kann man mit den folgenden zwei Befehlen bewerkstelligen statt mit find und exec zu arbeiten.

chmod 755 **/*(/)
chmod 644 **/*(.)

Zusätzlich gibt es auch Qualifier für verschiedenste Dateirechte. Folgender Befehl listet alle Dateien auf, die von allen beschrieben werden können:

ls **/*(.W)

Auch nach der Dateigröße lässt sich suchen:

print **/*(L0)

listet alle leeren Dateien auf.

Auch die Sortierung lässt sich beliebig anpassen. Mit dem Befehl:

print *(oL)

Listet alle Dateien der Größe nach von klein nach groß auf. Mit großem O wird die Sortierung umgekehrt.

Somit lässt sich mittels globbing in der zsh das Kommando find quasi komplett ersetzen. Die hier gezeigten Beispiele sehen auf den ersten Blick recht kompliziert und kryptisch aus. Wenn man das Prinzip aber einmal verstanden hat und die wichtigsten Qualifier kennt ist es aber ganz einfach und sehr praktisch.

Es gibt noch jede Menge weiterer Qualifier und Möglichkeiten für das Globbing (z.B. nur das erste oder eine bestimmte Anzahl von Elementen auszuwählen), die ich hier gar nicht alle erwähnen kann. Im zsh-Manual gibt es jedoch eine Liste der Glob-Qualifier.

Um alle Möglichkeiten nutzen zu können sollte die Option extended_glob gesetzt werden.

Vervollständigung
Die zsh hat wie die Bash einen Vervollständigungsmechanismus, so dass man mit der TAB-Taste Kommandos, Dateinamen und vieles mehr vervollständigen kann. In der zsh ist dieser Mechanismus jedoch extrem gut konfigurierbar und programierbar. So kann man z.B. beim scp Befehl (der Dateien über ssh kopiert) die Verzeichnisse auf dem entfernten Rechner vervollständigen und genau definieren welche Programme mit welchen Dateitypen zusammen arbeiten können. Für sehr viele Programme ist dies schon vorgefertigt, so dass ein

latex doku<TAB>

automatisch zu

 latex dokument.tex

vervollständigt wird. Dies beherrscht die Bash zum Teil auch. Jedoch ist es in der zsh wesentlich besser konfigurierbar und anpassbar.

Sehr praktisch finde ich auch die Menüfunktion. So wird beim ersten Druck auf die TAB-Taste wie in der Bash soweit vervollständigt, bis nicht mehr entschieden werden kann welche Datei (bzw. welcher Befehl) gemeint ist. Ein weiterer Druck auf die TAB-Taste listet alle weiteren Möglichkeiten auf und jeder weitere Druck geht diese Möglichkeiten durch. Wenn ich also drei Dateien habe: test.txt, testfile1.txt und testfile2.txt und gebe folgendes ein:

ls te<TAB>

so wird zuerst auf test vervollständigt. Ein weiterer Druck gibt mir alle Möglichkeiten aus:

testfile1.txt  testfile2.txt  test.txt

und ein dritter Druck auf die Tab-Taste vervollständigt den Befehl zu

ls testfile1.txt

Das ist viel schneller als erst ein f einzugeben um erneut TAB zu drücken und dann die 1 einzugeben um wiederum TAB zu drücken bis der Dateiname endgültig vervollständigt wird.

Ein weiterer Druck auf TAB nimmt dann die nächste Möglichkeit. In diesem Beispiel also

ls testfile2.txt

und so weiter.

Expansion
Mit der TAB-Taste kann man in der zsh nicht nur Kommandos, Dateinamen, … vervollständigen, sondern auch Variablen expandieren. Viele kennen ja die spezielle Variable !!, die den zuletzt ausgeführten Befehl enthält. In der zsh kann man nun folgendes eingeben:

!!<TAB>

und die Variable !! wird automatisch zum zuletzt ausgeführten Befehl expandiert. Genauso kann man auch jede andere Variable expandieren:

$PWD<TAB>

wird zu /home/zimon/developement (vorrausgesetzt ich befinde mich zum Zeitpunkt der expansion in diesem Verzeichnis).

History Kontrolle
Wie bei der Bash auch, kann man in der zsh die History durchsuchen. Die zsh geht jedoch so weit, dass man auch nach bestimmten Parametern suchen kann. Ein Beispiel:

cp ../perl/foo.pl ~/developement/projektordner

Danach kommen ein paar andere Kommandos. Möchte nun eine andere Datei in den gleichen Projektordner kopieren reicht folgende Eingabe:

cp ../perl/bar.pl !?proj?:3

Mit !? wird nach einem vorherigen Befehl gesucht. Das proj ist ein String der in diesem Befehl vorkam. Mit dem ?:3 wird das dritte Argument genutzt. Durch die Expansion kann manmit durch einen Druck auf TAB diesen Ausdruck auch noch expandieren um sicher zu gehen, dass das richtige Argument ausgewählt wurde. Man kann zwar mit Alt+. wie in der Bash die jeweils letzten Argumente durchgehen, auf diese Weise kann man aber auch das zweite von drei Argumenten suchen.

Es gibt noch wesentlich mehr Möglichkeiten die History zu durchsuchen und zu verwenden.

Hochkonfigurierbarer Prompt
In der Z-Shell kann man nicht nur die linke Seite des Prompts sondern auch die rechte Seite konfigurieren. Als Beispiel hier mal mein aktueller Prompt:

zsh Prompt

Mein aktueller zsh-Prompt

Ich finde die Anzeige der Uhrzeit inklusive Sekunden recht praktisch, da man daran sieht wie lange ein Befehl gedauert hat oder wann man ihn ausgeführt hat. Es gibt jedoch auch die Möglichkeit automatisch die Ausgabe von time bei der Beendigung eines Befehls ausgeben zu lassen, der länger als eine vorher definierte Zeit benötigt hat.

Der Prompt ist eine abgeänderte Version von Phil!s ZSH Prompt.

[UPDATE] Auf Anfrage hin ist hier noch der Code für die von mir abgeänderte Version des Prompts:

setprompt () {
    ###
    # Need this so the prompt will work.
 
    setopt prompt_subst
 
 
    ###
    # See if we can use colors.
 
    autoload colors zsh/terminfo
    if [[ "$terminfo[colors]" -ge 8 ]]; then
	colors
    fi
    for color in RED GREEN YELLOW BLUE MAGENTA CYAN WHITE; do
	eval PR_$color='%{$terminfo[bold]$fg[${(L)color}]%}'
	eval PR_LIGHT_$color='%{$fg[${(L)color}]%}'
	(( count = $count + 1 ))
    done
    PR_NO_COLOUR="%{$terminfo[sgr0]%}"
 
 
    ###
    # See if we can use extended characters to look nicer.
 
    typeset -A altchar
    set -A altchar ${(s..)terminfo[acsc]}
    PR_SET_CHARSET="%{$terminfo[enacs]%}"
    PR_SHIFT_IN="%{$terminfo[smacs]%}"
    PR_SHIFT_OUT="%{$terminfo[rmacs]%}"
    PR_HBAR=${altchar[q]:--}
    PR_ULCORNER=${altchar[l]:--}
    PR_LLCORNER=${altchar[m]:--}
    PR_LRCORNER=${altchar[j]:--}
    PR_URCORNER=${altchar[k]:--}
 
    if [[ "$TERM" == "screen" ]]; then
	PR_HBAR=-
     	PR_ULCORNER=--
    	PR_LLCORNER=--
    	PR_LRCORNER=--
    	PR_URCORNER=-
   fi 
 
 
    ###
    # Decide if we need to set titlebar text.
 
    case $TERM in
	xterm*)
	    PR_TITLEBAR=$'%{\e]0;%(!.-=*[ROOT]*=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\a%}'
	    ;;
	screen)
	    PR_TITLEBAR=$'%{\e_screen \005 (\005t) | %(!.-=[ROOT]=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\e\\%}'
	    ;;
	*)
	    PR_TITLEBAR=''
	    ;;
    esac
 
 
    ###
    # Decide whether to set a screen title
    if [[ "$TERM" == "screen" ]]; then
        PR_STITLE=$'%{\ekzsh\e\\%}'
    else
        PR_STITLE=''
    fi
 
 
    ###
    # Finally, the prompt.
 
    PROMPT='$PR_SET_CHARSET$PR_STITLE${(e)PR_TITLEBAR}\
$PR_RED$PR_SHIFT_IN$PR_ULCORNER$PR_BLUE$PR_HBAR$PR_SHIFT_OUT(\
$PR_GREEN%(!.%SROOT%s.%n)$PR_GREEN@%m:%l\
$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_RED$PR_HBAR${(e)PR_FILLBAR}$PR_BLUE$PR_HBAR$PR_SHIFT_OUT(\
$PR_MAGENTA%$PR_PWDLEN<...<%~%<<\
$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_RED$PR_URCORNER$PR_SHIFT_OUT\
 
$PR_RED$PR_SHIFT_IN$PR_LLCORNER$PR_BLUE$PR_HBAR$PR_SHIFT_OUT(\
%(?..$PR_LIGHT_RED%?$PR_BLUE:)\
${(e)PR_APM}$PR_YELLOW%D{%H:%M:%S}\
$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_RED$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_NO_COLOUR '
 
    RPROMPT=' $PR_RED$PR_SHIFT_IN$PR_HBAR$PR_BLUE$PR_HBAR$PR_SHIFT_OUT\
($PR_YELLOW%D{%a,%b%d}$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_RED$PR_LRCORNER$PR_SHIFT_OUT$PR_NO_COLOUR'
 
    PS2='$PR_RED$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_BLUE$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT(\
$PR_LIGHT_GREEN%_$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\
$PR_RED$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT$PR_NO_COLOUR '
 
}
 
setprompt

[/UPDATE]

ZLE-Widgets
Der Z-Line-Editor (ZLE) ist quasi die Eingabezeile in der zsh. Dafür kann man sich kleine Programme schreiben, die einem das Leben vereinfachen. Ein Beispiel wäre ein Programm, dass bei der Eingabe von ... diese zu ../.. umwandelt. Jeder weitere Punkt wird wieder umgewandelt. So kann man recht einfach folgendes eingeben:

cd ..../directory

Auf dem Bildschirm erscheint jedoch folgendes:

cd ../../../directory

Dieses Widget ist kein Standard bei der Z-Shell. Man kann es aber durch folgenden Code in der Konfigurationsdatei ~/.zshrc erzeugen:

rationalise-dot() {
    if [[ $LBUFFER = *.. ]]; then
        LBUFFER+=/..
    else
        LBUFFER+=.
            fi
}
zle -N rationalise-dot
bindkey . rationalise-dot

Automatisches tee-ing
In der Z-Shell kann man die Ausgabe eines Kommandos recht einfach in mehrere verschiedene Dateien umleiten. Statt wie in der Bash

ls | tee -a all | tee actual

zu schreiben, kann man in der zsh einfach

ls >>all >actual

eingeben. Beide Befehle führen ls aus und hängen die Ausgabe an die Datei all an und schreiben sie in die Datei actual wobei der inhalt jeweils überschrieben wird.
Möchte man die Ausgabe wie beim original tee Befehl auch auf dem Bildschirm sehen, so leitet man sie auch noch auf STDOUT um:

ls >>all >actual >&1

Verzeichnisse durch Ersetzung wechseln
Ein Beispiel sollte dies am besten Erklären. Man nehme an man hat zwei Verzeichnisse: ~/developement/scripting/perl/modules und ~/developement/scripting/python/modules und man befindet sich in ~/developement/scripting/perl/modules, so kann man durch den Befehl

cd perl python

in das Verzeichnis ~/developement/scripting/python/modules wechseln. Bei einem cd Kommando mit zwei Parametern wird im aktuellen Verzeichnis-String das erste Wort durch das zweite ersetzt. Hier also perl durch python.

Fazit
Ich bin jetzt nicht 100% sicher, dass alles hier beschriebene nicht auch in der Bash möglich ist. Es zeigt aber auf jeden Fall, dass die Z-Shell jede Menge interessante Möglichkeiten bietet und man viele Dinge einfacher und schneller erledigen kann als in der Bash. Die oben gezeigten Features sind meist nur angerissen und nur eine kleine Auswahl von dem was die Z-Shell bietet. Darüber hinaus besitzt die Z-Shell eine ganze Reihe Module z.B. für Matheoperationen oder einen eigenen FTP-Client, die man bei Bedarf laden kann.

Sehr schön ist auch die Möglichkeit den Screen-Title von der zsh aus zu setzen.

Es lohnt sich auf jeden Fall, diese Shell etwas genauer zu betrachten. Bei mir hat sie mittlerweile die Bash fast vollständig ersetzt (vorhandene Scripte habe ich nicht umgeschrieben).

Links

Gimp 2.7 mit Single-Window-Mode installieren

21. Dezember 2009 von zimon

Ab der nächsten Version von Gimp (2.8) gibt es endlich auch die Möglichkeit des Einfenster-Betriebs.

Screenshot des Single-Window Modus von Gimp 2.7

Screenshot des Single-Window Modus von Gimp 2.7


Im Moment gibt es in Gimp je ein Fenster für jedes geöffnete Bild und zusätzlich ein Fenster mit den Werkzeugen (Werkzeugkasten) sowie bei Bedarf auch Dialogfenster für Ebenen, Pfade, … Der Umgang mit so vielen Fenstern ist auf kleinen Bildschirmen recht umständlich, da oft ein Fenster im Weg steht und man es zuerst weg schieben muss. Daher freuen sich schon viele Nutzer auf den Einfenster Modus, bei dem Bilder als Tabs angezeigt werden und die restlichen Fenster links und rechts neben dem Bild angeordnet werden. Dialogfenster können jedoch immer noch heraus gezogen und als eigenes Fenster angezeigt werden.

Es gibt jedoch auch Beführworter des Multi-Window Modus, da man mehrere Fenster leichter auf verschiedene Bildschirme verteilen kann.

Ich habe mir mal die Entwicklerversion (2.7) installiert und (vor allem den Single-Window Modus :) ) angesehen.

Installation

Zuerst müssend die build-utils, git sowie einige Abhängigkeiten installiert werden. Unter Ubuntu kann man dies mit dem folgenden Befehl erreichen:

sudo apt-get build-dep gimp libtool ruby git-core git-doc git-email

Als nächstes wird git konfiguriert. Dafür die folgenden beiden Befehle ausführen, wobei beim ersten Befehl der Name eingegeben wird und beim zweiten die Emailadresse:

git config --global user.name "Your Name"
git config --global user.email you@host

Nun werden die aktuellen Entwickerversionen von babl, gegl und gimp ausgecheckt:

git clone git://git.gnome.org/babl
git clone git://git.gnome.org/gegl
git clone git://git.gnome.org/gimp

Um eine bereits installiertes Gimp weiter benutzen zu können, wird die Entwicklerversion in das Verzeichnis /usr/local/gimp-git installiert.
Dafür wird dieses Verzeichnis angelegt und dort ein Verzeichnis share erstellt. Nun legt man die Datei /usr/local/gimp-git/share/config.site mit folgendem Inhalt an:

export PKG_CONFIG_PATH="/usr/local/gimp-git/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="/usr/local/gimp-git/lib:$LD_LIBRARY_PATH"

Wenn es Probleme gibt, dass die neueste babl-Version nicht gefunden werden kann, können die beiden Befehle auch nochmal in der aktuellen Shell ausgeführt werden.

Die Leserechte müssen auch noch entsprechend gesetzt werden:

chmod -R oug+rx /usr/local/gimp-git
chmod oug-x /usr/local/gimp-git/share/config.site

Nun werden babl, gegl und schließlich gimp selbst kompiliert und installiert:

cd babl
./autogen.sh --prefix=/usr/local/gimp-git
make
sudo make install
 
cd ../gegl
./autogen.sh --prefix=/usr/local/gimp-git
make
sudo make install
 
cd gimp
./autogen.sh --prefix=/usr/local/gimp-git --disable-gtk-doc
make
sudo make install

Nun kann man gimp-2.7 mit folgendem Befehl starten:

/usr/local/gimp-git/bin/gimp-2.7

Ein Alias in der ~/.bashrc erleichtert den Aufruf:

alias gimp-git='/usr/local/gimp-git/bin/gimp-2.7'

Single-Window-Mode

Nachdem ich alle Menüs in den Einstellungen durchforstet habe und auch in einige Konfigurationsdateien geschaut habe, habe ich die Funktion für den Single-Window-Mode schließlich unter dem Menüpunkt “Fenster” gefunden.

Es sieht schon ziemlich schick aus. Auch wenn viele Kleinigkeiten noch nicht so richtig Funktionieren. Wenn die Dialoge an der Seite sind, reagieren sie manchmal nicht (oder nur teilweise). Abhilfe schaft auch manchmal den Modus wieder zurück zu wechseln und erneut in den Single-Window Modus zu wechseln. Versucht man einen Dialog unter den Werkzeugkasten (oder an eine andere Stelle an der Seite) zu platzieren, stürzt das ganze Programm mit einem segmentation fault ab.

Im Moment ist es noch sehr emfehlenswert, bei der Version 2.6 zu bleiben und die Entwicklerversion nur zum Ausprobieren zu nutzen. Dies gilt besonders für den Single-Window Modus, da dieser noch nicht sehr stabil läuft. Das wird sich aber sicherlich in der nächsten Zeit noch ändern.

[UPDATE]Mittlerweile funktioniert der Single-Window Modus recht gut und stabil, so dass man ihn nun verwenden kann. Man sollte jedoch im Hinterkopf behalten, dass sich das Programm noch im Entwicklungsstadium befindet und Fehler auftreten können.

Übrigens soll Gimp 2.8 frühestens Ende des Jahres (2010) erscheinen, wie man der Developer-Mailingliste entnehmen kann.[/UPDATE]

Den Screenshot in voller Auflösung habe ich hier verlinkt (am besten in einem nuen Tab oder Fenster öffnen).

Erinnerungen mit at und zenity

11. Dezember 2009 von zimon

Wenn man am PC sitzt vergisst man schnell die Zeit. Wenn man noch etwas erledigen muss wäre es daher vorteilhaft, wenn man daran erinnert werden würde.

Ein solches “Erinnerungsprogramm” kann man sich recht leicht selber basteln. Ich habe dafür das Linuxtool at mit dem Programm zenity, welches Dialoge darstellen kann, kombiniert.

at (bzw. der at-Daemon atd) ist auf den meisten Linuxdistributionen bereits vorhanden, kann sonst aber auch über die Paketverwaltung installiert werden. Zenity sollte bei den meisten Distributionen ebenfalls über die Paketverwaltung installierbar sein.
Unter Ubuntu installiert man zenity mit dem Befehl:

sudo apt-get install zenity

Mit at können Befehle zu einem festgelegten Zeitpunkt ausgeführt werden. Eine sehr schöne Einführung gibt es in der Ausgabe 10/2008 von freiesMagazin (eine Fortsetzung ist in der nachfolgenden Ausgabe enthalten, welche mich auch zu diesem “Programm” inspirierte, weil ich mit den dort vorgestellten Erinnerungs-Lösungen nicht zufrieden war).

Um mit zenity einen Dialog anzuzeigen kann man folgenden Befehl nutzen:

zenity --info --title="Titel des Dialogs" --text "Text, der angezeigt werden soll"

Damit dies von at angezeigt werden kann muss noch das Display angegeben werden:

zenity --info --title="Titel des Dialogs" --text "Text, der angezeigt werden soll" --display=:0

Diesen Befehl kann man nun in ein Bash- oder Perl-Script einfügen, wobei der Titel fest sein kann und der Text als Argument übergeben wird. Ich habe mich für Perl entschieden:

#!/usr/bin/perl
 
system("zenity --info --title=\"Erinnerung\" --text \"@ARGV\" --display=:0");

Dieses Script habe ich unter dem Namen r (für “remember”) im bin-Ordner meines Homeverzeichnisses gespeichert. Dieses muss natürlich im Pfad liegen, was durch folgende Zeile in der Datei ~/.bashrc erreicht werden kann:

export PATH=$PATH:/home/user/bin

“user” muss natürlich durch den entsprechenden Usernamen ersetzt werden. Nachdem man die ~/.bashrc mit dem Befehl

. ~/.bashrc

neu “geladen” hat kann man sich nun an Termine oder andere Dinge erinnern lassen.

Beispiele:
Um 14:30 Termin bei FooBar. Also um 14:15 erinnern:

at 14:15
r Termin bei FooBar in 15 Minuten
Strg+d

In einer halben Stunde ist eine Besprechung (5 min vorher erinnern):

at now +25min
r Besprechung
Strg+d

Es öffnet sich zur angegebenen Zeit ein Dialogfenster (egal, was man gerade macht – solange man sich in X befindet. Bei Spielen im Fullscreen habe ich es jedoch nicht getestet.), welches den Titel “Erinnerung” enthält und den angegebenen Text ausgibt. Das Fenster kann durch einen Klick auf “OK” wieder geschlossen werden.

at bietet noch viele weitere Möglichkeiten eine Zeit anzugeben. Diese werden im (ersten) oben verlinkten freiesMagazin-Artikel beschrieben.

Titel von Screen-Fenstern automatisch von Vim, Bash und zsh setzen

3. Oktober 2009 von zimon

Wenn man unter Screen die Statusleiste nutzt, ist es oft mühsam jedes mal die Titel zu setzen. Auch wenn man diese fest in der ~/.screenrc definiert, kann man manchmal durcheinander kommen.

Unter Vim gibt es die Möglichkeit, den Namen der aktuellen Datei als Titel für das Screenfenster zu benutzen.
Dafür braucht man nur die folgenden Zeilen zur ~/.vimrc hinzufügen:

if expand('$TERM') == 'screen'
augroup vim_screen
  autocmd!
  autocmd VimEnter,BufWinEnter,WinEnter *
    \ exec "silent! !echo -ne '\\ek" . expand('%:t') . "\\e\\\\'"
augroup END
endif

[UPDATE]Auf dem 256bit Blog beschreibt Christian Brabandt, wie man den Screen-Titel von Vim aus ohne autocommands und mit sehr vielen Einstellungen setzen kann.[/UPDATE]

Ansonsten finde ich es sehr praktisch, wenn in der Statusleiste das aktuelle Verzeichnis steht. Da der komplette Pfad meist sehr lang ist, können mit einem regulären Ausdruck alle Verzeichnisse bis auf das letzte jeweils mit ihren Anfangsbuchstaben abgekürzt werden:

s/(\w).*?\//$1\//g

So wird /home/zimon/.vim/plugin zu /h/z/.v/plugin abgekürzt.

Wem das immer noch zu lang ist, der kann aus dem Namen des letzten Verzeichnisses auch noch die Vokale heraus nehmen (ähnlich wie es dillo in seinen Tabs macht):

s/(\w).*?\//$1\//g; my $end; $x=~s/\/(.)(?:[aeiou]|([^aeiou\/])(?{$end.=$^N}))*\s*$/\/$1$end/

Damit wird /home/zimon/.vim/plugin zu /h/z/.v/plgn

Um in der Bash den Screen-Titel automatisch zu setzen, kann man folgenden Code zu seiner ~/.bashrc hinzufügen. Hier wurde der erste reguläre Ausdruck genutzt (man kann ihn natürlich auch durch den anderen Ausdruck ersetzen, wenn man möchte):

function precmd {
    case "$TERM" in
        screen)
        wd=`perl -e '$x=shift; $x=~ s/(\w).*?\//$1\//g; print $x;' "$PWD"`
        echo -ne "\033k$wd\033\\"
        ;;
    esac
}
PROMPT_COMMAND=precmd

Für zsh User gibt es hier den Code für die ~/.zshrc. In diesem Beispiel wurde der zweite reguläre Ausdruck genutzt:

function precmd {
    if [[ "$TERM" == "screen" ]]; then
    local wd=`perl -e '$x=shift; $ x=~ s/(\w).*?\//$1\//g; my $end; $x=~s/\/(.)(?:[aeiou]|([^aeiou\/])(?{$end.=$^N}))*\s*$/\/$1$end/; print $x;' "$PWD"`
	PR_STITLE=$'%{\ek'$wd$'\e\\%}'
    fi
}

Vielen Dank an Ploppor für den zsh Code und sonstige zahlreiche Unterstützung in letzter Zeit.

Es tut mir leid, dass ich in letzter Zeit nichts geschrieben habe, aber meine Diplomarbeit lässt mir einfach keine Zeit.

Vim Plugin – VCSCommand zur Versionsverwaltung mittels SVN und CVS

8. August 2009 von zimon

Mit dem Plugin VCSCommand kann man Dateien und Verzeichnisse direkt von Vim aus über ein SVN- oder CVS-Repository verwalten. Ich werde hier jedoch nur SVN behandeln, da ich momentan nur dieses System nutze.

Nachdem man die Dateien des Plugins nach ~/.vim/plugin/ kopiert hat, kann man die Befehle :VCSUpdate und :VCSCommitnutzen um die aktuelle Datei zu Updaten bzw. seine Änderungen zu committen. Dafür muss die Datei jedoch bereits unter Versionskontrolle stehen.
Beim Commit öffnet sich ein Splitscreen, in den man die Logmessage eintragen kann. Mittels :wq oder \cc wird diese übernommen. Um ohne Logmessage zu Committen, kann man das Kommando mit einem Ausrufezeichen aufrufen: :VCSCommit!.

Es gibt auch Abkürzungen für die Kommandos. So kann man statt :VCSCommit auch den Befehl \cc nutzen, der das gleiche bewirkt. Genauso kann man mit \cu updaten.

Auch die anderen Kommandos sind im Prinzip die SVN Kommandos mit vorrangestelltem VCS. In Klammern steht jeweils die Kurzvariante:

  • :VCSAdd – (\ca) zum Hinzufügen von Dateien zum Repository
  • :VCSDelete – (\cD) zum Löschen
  • :VCSStatus – (\cs) um den Status der Datei anzuzeigen
  • :VCSDiff – (\cd) um den Unterschied zur letzten Version anzuzgeigen
  • :VCSLog – (\cl) zeigt die bereits getätigten commits mit ihren Logmessages an

Statt :VCSDiff kann man auch :VCSVimDiff (\cv) nutzen um die Unterschiede mittels Vimdiff zu betrachten.

Um ganze Verzeichnisse mit dem CVSCommand-Plugin zu verwalten, kann man die Dateibrower-Funktion des Vim nutzen und die entsprechenden Kommandos bei einem geöffneten Verzeichnis ausführen.

Es gibt natürlich noch weitere Kommandos und Einstellungsmöglichkeiten. Diese sind in der Hilfe näher beschrieben, die man mittels :h vcscommand aufrufen kann.

Scenariogenerator für VNUML

23. Juli 2009 von zimon

Vor einiger Zeit habe ich mal einen Scenariogenerator für VNUML geschrieben um schnell und einfach Scenariodateien für verschiedene Topologien erstellen zu können.

Dafür habe ich mir überlegt, was die wesentlichen Informationen sind um eine Netzwerktopologie zu beschreiben. Dies sind die existierenden Netze, die existierenden Router und welche Router an welche Netze angeschlossen sind.
Dem Generatorscript wird eine Datei mit diesen Informationen übergeben und erstellt daraus die VNUML-Scenariodatei. Über Variablen im Script kann gesteuert werden, welcher Kernel und welches Dateisystem verwendet werden soll und wie die einzelnen Router/Rechner-Definitionen aussehen sollen.

Syntax
Die Syntax der übergebenen Dateien, die die Endung .zvf tragen müssen ist folgendermaßen:
Leerzeilen sowie Zeilen, die mit # beginnen werden ignoriert.
In der ersten Zeile stehen die Netze mit Komma getrennt (Leerzeichen sind nicht erlaubt)
In den darauf folgenden Zeilen werden die Router definiert und angegeben, mit welchen Netzen sie verbunden sind. Am Anfang einer solchen Zeile steht jeweils zuerst der Name des Routers gefolgt von einem Leerzeichen. Danach werden alle angeschlossenen Netze mit Komma getrennt aufgelistet (Leerzeichen sind darin nicht erlaubt).

Beispiel
Das folgende Beispiel definiert ein Scenario mit 3 Routern (r1, r2 und r3), die über 3 Netze (net1,net2 und net3) zu einem Dreieck verbunden sind:

# beispiel.zvf
net1,net2,net3
 
r1 net1,net2
r2 net2,net3
r3 net3,net1

Die Namenskonvention (/net[0-9]+/ für Netze und /r[0-9]+/ für Router) muss eingehalten werden, damit das Script funktioniert.
Da keine Fehlerbehandlung implementiert ist, muss die Syntax exakt eingehalten werden.

Wenn man das Script mit chmod a+x createscenario.pl ausführbar gemacht hat, kann man die Scenariodatei beispiel.xml durch folgenden Aufruf erstellen:

./createscenario.pl beispiel.zvf

Download
Hier das Perlscript zum downloaden:

createscenario.pl

Irgendwie gibt es Probleme, wenn ich eine .pl Datei direkt verlinke. Daher habe ich sie in createscenario.txt umbenannt. Nach dem Download sollte die Datei also mit dem Befehl

mv createscenatio.txt createscenatio.pl

umbenannt werden.

Es können Maximal 254 Netze und 254 Router erstellt werden. Die IP-Adressen werden nach folgendem Schema vergeben:
10.0.NETZNUMMER.ROUTERNUMMER wobei ROUTERNUMMER pro Netz hoch gezählt wird und nicht der Nummer des Routernamens entspricht (die Netznummer hingegen ist auch die Nummer aus dem Namen des Netzes).

Vim – Buffer statt Tabs benutzen

18. Juli 2009 von zimon

Das Buffer-Konzept von Vim ist ein wenig gewöhnungsbedürftig. Unter vim wird jede Datei in einem Buffer geöffnet. Man kann auch mehrere Dateien in verschiedenen Buffern öffnen und zwischen diesen Buffern hin- und herschalten. Die einzelnen geöffneten Buffer müssen dabei nicht sichtbar sein.

Das Problem bei Tabs ist, dass wenn man z.B. Dateiübergreifende Markierungen nutzt der Sprung in eine andere Datei den entsprechenden Buffer im aktuellen Tab geöffnet wird. Selbst wenn die Datei in einem anderen Tab bereits geöffnet ist wird nicht zum Tab gesprungen. Wenn man hingegen nur mit Buffern statt Tabs arbeitet ist dieses Verhalten gewünscht.
Auch bei Splitscreens werden die Dateien in verschiedenen Buffern geladen.

Einen neuen Buffer öffnet man mit :n <DATEINAME>. Um den Buffer zu wechseln kann man entweder :bn (Buffer Next) und :bp (Buffer Previous) oder :b<NUMMER> nutzen, wobei NUMMER die Nummer des Buffers angibt. [UPDATE]Mit :b# (alternativ Strg+^) welchselt man zum zuletzt editierten Buffer. [/UPDATE] Alle geöffneten Buffer kann man sich mit :ls anzeigen lassen.

Etwas gewöhnungsbedürftig ist, dass man einen Buffer nur dann wechseln kann, wenn der aktuelle Buffer keine Änderungen enthält. Man muss also immer speichern, bevor man den Buffer wechseln kann. Um diese Restriktion zu umgehen kann man die entsprechenden Kommandos mit Ausrufezeichen verwenden. z.B. :bn!.[UPDATE]Statt dessen kann man auch die Option set hidden setzen (z.B. indem man dieses als eine Zeile in die ~/.vimrc schreibt). (Danke an b23 für den Tipp.)[/UPDATE]
Auch das ein Buffer nicht automatisch durch ein :q geschlossen wird ist ungewohnt. Dafür nutzt man den Befehl :bd (Buffer Delete).

Da das Konzept der Tabs recht praktisch ist und man auch mit Buffern oft eine permanente Übersicht über die offenen Dateien haben möchte gibt es das Plugin MiniBufExplorer. Dieses Plugin zeigt in einem einzeiligen horizontalen Splitscreen alle geöffneten Buffer an. Das Plugin bietet auch einige Funktionen an um die Buffer zu wechseln, doch ich empfinde es als einfacher, sich eigene Mappings zu definieren.

[UPTDATE]Ein weiteres praktisches Plugin ist bufexplorer, welches keinen Splitscreen öffnet. Statt dessen kann man sich die Buffer mit \be anzeigen lassen und einen aussuchen (mit \bs werden die Buffer in einem Splitscreen angezeigt, mit \bv in einem vertikalen splitscreen). Wenn man (ohne dass die Option hidden gesetzt ist) von einem ungespeicherten Buffer wechseln möchte, so wird automatisch ein Splittscreen geöffnet (bei \bv ein Vertikaler) in dem der Buffer angezeigt wird, zu dem gewechselt wurde. (Danke an b23 für den Hinweis)[/UPDATE]

Wenn man (wie ich) daran gewohnt ist, die Tabs mit Strg+n durchzugehen, ist es ganz praktisch, die selbe Tastenkombination zum wechseln der Buffer zu verwenden. Dafür fügt man folgende Zeile in die ~/.vimrc ein oder ändert die entsprechende Zeile, wenn man Strg+n schon für Tabs definiert hat:

map <C-n> :bn!<CR>

Alles in allem haben beide Systeme ihre Vor- und Nachteile. Tabs sind intuitiver und man braucht kein Plugin um sie anzuzeigen. Bei Buffern hat man mit dem MiniBufExplorer-Plugin immer einen horizontalen Splitscreen geöffnet, der manchmal störend sein kann. Dafür sieht man bei Buffern genau, welche Dateien geöffnet sind. Wenn man nur mit Tabs arbeitet sind nach einiger Zeit viele Dateien in Buffern geöffnet ohne das man davon weiß. Es gibt auch einige Plugins, die sich mit Buffern besser nutzen lassen als mit Tabs.

Ich denke man sollte mit beiden Systemen vertraut sein und das jeweils geeignetere nutzen je nachdem, was man vor hat.
Wenn man z.B. standardmäßig mit Tabs arbeitet, kann man in einem geöffneten Splitscreen mit Bufferkommandos zwischen den Dateien wechseln.

Vim Plugin – Surround umschließt Text mit Klammern, Anführungszeichen und HTML-Tags

14. Juli 2009 von zimon

Für Vim gibt es ein sehr praktisches Plugin namens Surround, welches es einem ermöglicht Wörter, Sätze, Absätze, Zeilen,… mit Klammern, Anführungszeichen oder XML-Tags zu umschließen. Solche Umschließungen können auch geändert oder gelöscht werden.

Zur Installation wird das Archiv surround.zip im Verzeichnis ~/.vim entpackt.

Grundsätzlich lehnt sich die Bedienung an das Konzept an, den Text zwischen Klammern, Anführungszeichen,… zu ändern so wie es im Artikel Vim Bewegungskommandos im Abschnitt “Spezielle Bewegungskommandos” beschrieben ist.

Text Umschließen
Die Syntax zum Umschließen von Text ist:
ys<BEWEGUNG><ZEICHEN>

Beispiele:

  • ysis" – der aktuelle Satz wird mit doppelten Anführungszeichen umschlossen.
  • ysiw<b> – versieht das aktuelle Wort mit mit dem HTML-Tag b, so dass es fett angezeigt wird.
  • yss) – umschließt die aktuelle Zeile mit runden Klammern.
  • ysi)" – umschließt den Text zwischen den Klammern mit doppelten Anführungszeichen.
  • ysa") – umschließt den den Text zwischen den doppelten Anführungszeichen und die Anführungszeichen selbst mit runden Klammern.

Statt den Bewegungskommandos kann man auch im visuellen Modus den gewünschten Text markieren. Dann drückt man s gefolgt vom gewünschten Zeichen.
Beispiel: s" um den markierten Text mit doppelten Anführungszeichen zu umschließen.

Damit man es sich besser vorstellen kann hier ein konkretes Beispiel. Die folgende Zeile:

Dies ist eine Beispielzeile

wird durch yss" zu

"Dies ist eine Beispielzeile"

Liegt der Cursor auf dem Wort “eine”, so kann man mit ysiw) das Wort mit Klammern umschließen:

"Dies ist (eine) Beispielzeile"

Umschließung ändern
Um die Umschließung von Text zu ändern nutzt man folgende Syntax:
cs<AKTUELLES_ZEICHEN><NEUES_ZEICHEN>

Beispiele:

  • cst" – ändert das XML/HTML-Tag zu doppelten Anführungszeichen.
  • cs"' – ändert doppelte in einfache Anführungszeichen.
  • cs)} – ändert runde in geschweifte Klammern.

Umschließung löschen
Die Syntax um eine Umschließung zu löschen ist:
ds<ZEICHEN>

Beispiele:

  • ds] – löscht eckige Klammern.
  • ds' – löscht einfache Anführungszeichen.
  • dst – löscht XML/HTML-Tags.

Wiederholen
Leider funktioniert das Wiederholen des letzten Kommandos mit Punkt nicht mit dem Surround-Plugin. Jedoch kann man es mit dem Plugin repeat zum funktionieren bringen.
Dafür kopiert man die Datei repeat.vim in das Verzeichnis ~/.vim/autoload/. Nachdem man . (Punkt) gedrückt hat muss man jedoch das Zeichen, welches eingefügt werden soll wiederholen.

Abhilfe schafft ansonsten auch die Nutzung von Makros. Dabei können häufig genutzte Surroundings auch als Makro in die ~/.vimrc gespeichert werden.
Folgende Zeile definiert das Register k so, dass beim Aufruf des Makros k mittels @k das aktuelle Wort in runde Klammern eingeschlossen wird:

let @k="ysiw)"

Whois Plugin für den zBot

10. Juli 2009 von zimon

Vor einiger Zeit habe ich ein Whois-Plugin für meinen Jabber Bot zBot geschrieben. Aus irgendeinem Grund habe ich es nie online gestellt. Dies möchte ich hiermit nachholen. Ich habe den Code noch ein wenig verschönert.

Fauchi95 hat mich gefragt, ob man nicht ein Whois-Plugin schreiben könnte, da ist es mir wieder eingefallen :-)

Die Bedienung ist so wie man es sich denkt. Man sendet dem Bot ein whois gefolgt von der gewünschten Domain und bekommt das Ergebnis zurück gesendet.

Beispiel:

whois zinformatik.de

Zum Installieren des Plugins kopiert man die Datei Whois.pm in das Verzeichnis plugins im Hauptverzeichnis des zBot und startet dann den Bot neu.

Download:
Whois.pm

VNUML – Ein Netzwerksimulator mit User-Mode-Linux

27. Juni 2009 von zimon

Foto: Ben Stanfield

Foto: Ben Stanfield

VNUML (Virtual Network User-Mode-Linux) ist ein Programm um Netzwerke zu simulieren. Dabei werden die beteiligten Rechner nicht nur simuliert, sondern als virtuelle Maschinen gestartet, wodurch sie mit echten Protokollen miteinander kommunizieren. Daher eignet sich dieses Tool sehr gut zum Testen und Lernen z.B von Routingprotokollen. Man kann damit jedoch auch Paketfiltering mittels iptables sowie andere Dienste wie ARP, DNS oder ICMP ausprobieren. Auch eigene Entwicklungen können so getestet werden.
Da für jeden Rechner ein User-Mode-Linux System (englisch) – welches über einen echten Kernel und ein eigenes Dateisystem verfügt – gestartet wird, ist die Simulation sehr realitätsnah und die Rechner verhalten sich wie echte autonome Maschinen.
Für diesen Artikel ist es vorteilhaft, wenn man grundlegende Kenntnisse über Netzwerke besitzt (es wird z.B. vorausgesetzt, dass man weiß was IP-Adressen, Netze und Netzmasken sowie Router sind)

UPDATE: Artikel nochmal überarbeitet

Installation
Für Debian basierte Distributionen gibt es ein .deb Paket, welches zumindest unter Ubuntu problemlos installiert werden kann. Ansonsten kann man sich den Quelltext herunter laden. Eine englische Installationsanleitung ist auch verfügbar.
Ist VNUML installiert, braucht man noch ein Dateisystem. Auf der Projektseite gibt es 2 verschiedene zum downloaden. Ein größeres, welches mehr Programme beinhaltet und ein sehr abgespecktes, welches nützlich ist um größere Netzwerke zu generieren, da für jeden virtuellen Rechner ein Dateisystem im Arbeitsspeicher liegt.
Man kopiert das gewählte Dateisystem nach /usr/share/vnuml/filesystems/filesystem.img. Für die Beispiele in diesem Artikel reicht das kleine, welches man als mini_fs speichern kann. Dafür können die folgenden Befehle benutzt werden, nachdem man die Datei n3vlr-0.11-vnuml-v0.1.tar.gz herunter geladen hat:

tar -xvzf n3vlr-0.11-vnuml-v0.1.tar.gz
sudo cp n3vlr-0.11-vnuml-v0.1.img /usr/share/vnuml/filesystems/mini_fs

Möchte man das Dateisystem ändern um z.B. neue Programme hinzuzufügen oder unbenötigte Dateien zu löschen, kann man dieses mit dem Befehl

sudo mkdir /mnt/vnuml
sudo mount -o loop /usr/share/vnuml/filesystems/filesystem.img /mnt/vnuml

einhängen. Nun kann man im Ordner /mnt/vnunml alle gewünschten Änderungen vornehmen und das Dateisystem danach mit sudo umount /mnt/vnuml wieder aushängen.

Scenariodatei erstellen
Die Netzwerktopologie wird in einer XML-Datei beschrieben. In dieser werden zuerst die globalen Einstellungen festgelegt, in denen unter anderem angegeben wird, wo das zu nutzende Dateisystem liegt und welchen ssh-Key man nutzen möchte um auf die virtuellen Rechner zuzugreifen.
Danach folgen die Definitionen der Netze gefolgt von den Rechnern.
Im folgenden eine minimale Beispielkonfiguration, die zwei Rechner beschreibt, die über ein Netz miteinander verbunden sind. Zuerst der globale Teil, in dem die VNUML-Version, der Name des Scenarios und weitere Einstellungen definiert werden. Dort wird auch das Management-Netz (192.168.0.0/24) definiert, über das man vom eigenen Rechner auf die verschiedenen virtualisierten Maschinen zugreifen kann.
Existiert noch kein öffentlicher ssh-key, so sollte man sich mit folgendem Kommando einen anlegen:

sudo ssh-keygen

Bei der Frage nach einem Passwort drückt man einfach Enter ohne ein Passwort einzugeben. Der vorgeschlagene Pfad sollte stimmen, kann also beibehalten werden.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE vnuml SYSTEM "/usr/share/xml/vnuml/vnuml.dtd">
 
<vnuml>
    <global>
        <version>1.8</version>
        <simulation_name>beispiel</simulation_name>
        <ssh_version>2</ssh_version>
        <ssh_key>/root/.ssh/id_rsa.pub</ssh_key>
        <automac/>
        <vm_mgmt type="private" network="192.168.0.0" mask="24" offset="100" >
            <host_mapping/>
        </vm_mgmt>
        <vm_defaults exec_mode="mconsole">
            <filesystem type="cow">/usr/share/vnuml/filesystems/mini_fs</filesystem>
            <kernel>/usr/share/vnuml/kernels/linux</kernel>
        </vm_defaults>
    </global>

Als nächstes werden die Netze definiert. In diesem Beispiel gibt es nur ein Netz, da man sich sonst um das Routing kümmern müsste (dazu später mehr).

    <net name="net1" mode="virtual_bridge"/>

Im Modus “virtual_bridge” werden die Interfaces aller virtuellen Rechner sowie alle Netze über Bridges ans Hostsystem (den eigenen Rechner) gekoppelt, so dass man sie mit einem ifconfig sehen und mittels tcpdump oder Wireshark überwachen kann. Dafür wird jedoch das Paket “bridge-utils” benötigt. Alternativ kann man statt dessen auch “uml_switch” benutzen.

Nun folgen die Definitionen der virtuellen Rechner. Bei beiden Rechnern wird ein Interface definiert (es können aber auch mehrere sein, z.B. für Router).

    <vm name="rechner1">
        <if id="1" net="net1">
            <ipv4 mask="255.255.255.0">10.0.1.1</ipv4>
        </if>
    </vm>
 
    <vm name="r2">
        <if id="1" net="net1">
            <ipv4 mask="255.255.255.0">10.0.1.2</ipv4>
        </if>
    </vm>
</vnuml>

Es empfiehlt sich die Datei genauso wie das Scenario (die globale Eigenschaft simulation_name) zu benennen. In diesem Fall also beispiel.xml

Scenario starten und zu den Rechnern verbinden
Um das Scenario zu starten genügt der folgende Aufruf:

sudo vnumlparser.pl -t beispiel.xml

Nachdem das Scenario gestartet ist, kann man sich auf einen der Rechner einloggen:

sudo ssh rechner1

Durch die Angabe des öffentlichen ssh-Keys sollte es keine Passwortabfrage geben. Ansonsten ist das Passwort per default xxxx. Nun kann man alles mögliche Testen. Man kann einen Ping versenden:

ping 10.0.1.2

oder ein Interface überwachen.
Dafür loggt man sich am besten in einer anderen Konsole auf dem 2. Rechner ein:

sudo ssh rechner2

und startet dort einen TCP-Dump:

tcpdump -i eth1

Der Ping sollte die ganze Zeit weiter laufen, damit man auch etwas sehen kann. Danach kann man ihn mit Strg+c stoppen.
Mit exit kann man einen Rechner wieder verlassen.
Da man häufiger auf mehreren Rechnern gleichzeitig eingeloggt sein möchte wenn man mit VNUML arbeitet, bietet es sich an Screen zu benutzen.
Um die Simulation zu beenden führt man folgenden Befehl aus:

sudo vnumlparser.pl -P beispiel.xml

Dienste starten
Wie bereits erwähnt eignet sich VNUML besonders gut zum ausprobieren und Testen von Routingprotokollen. Daher sind auch die Quagga-Implementationen der bekanntesten Routingprotokolle RIP, OSPF und BGP bereits installiert und können recht einfach genutzt werden. Ich werde in diesem Artikel jedoch nur eine Minimalkonfiguration für RIP und OSPF angeben. Eine Einführung in diese Protokolle und deren Konfiguration würde an dieser Stelle zu weit führen.
Um das Routing grundsätzlich zu aktivieren wird das Tag

<forwarding type="ipv4" />

genutzt. Weitere Tags binden ein Konfigurationsverzeichnis conf ein (welches im gleichen Verzeichnis, wie die xml-Datei des Scenarios liegt) und starten die verschiedenen Daemons.
Im Verzeichnis conf sollten dann die folgenden Dateien vorhanden sein:

zebra.conf

!
hostname zebra
!
password xxxx
enable password xxxx

ripd.conf

!
hostname ripd
password xxxx
!
router rip
network 10.0.0.0/8

ospfd.conf

!
hostname ospfd
password xxxx
!
router ospf
network 10.0.0.0/8 area 0

Weitere Infos zu den Quagga-Routingprotokollen gibt es unter http://www.quagga.net.
Ein Scenario, welches der Einfachheit halber nur aus Routern besteht, auf denen man wahlweise OSPF oder RIP starten kann wird im folgenden Beispiel definiert:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE vnuml SYSTEM "/usr/share/xml/vnuml/vnuml.dtd">
 
<vnuml>
    <global>
        <version>1.8</version>
        <simulation_name>mini</simulation_name>
        <ssh_version>2</ssh_version>
        <ssh_key>/root/.ssh/id_rsa.pub</ssh_key>
        <automac/>
        <vm_mgmt type="private" network="192.168.0.0" mask="24" offset="100" >
            <host_mapping/>
        </vm_mgmt>
        <vm_defaults exec_mode="mconsole">
            <filesystem type="cow">/usr/share/vnuml/filesystems/mini_fs</filesystem>
            <kernel>/usr/share/vnuml/kernels/linux</kernel>
        </vm_defaults>
    </global>
 
    <net name="net1" mode="virtual_bridge"/>
    <net name="net2" mode="virtual_bridge"/>
 
    <vm name="r1">
        <if id="1" net="net1">
            <ipv4 mask="255.255.255.0">10.0.1.1</ipv4>
        </if>
 
        <forwarding type="ipv4" />
 
        <filetree root="/etc/quagga" seq="start">conf</filetree>
        <exec seq="start" type="verbatim">sysctl -w net.ipv4.conf.all.rp_filter=0</exec>
        <exec seq="start" type="verbatim">hostname</exec>
        <exec seq="start" type="verbatim">/usr/lib/quagga/zebra -f /etc/quagga/zebra.conf -d</exec>
        <exec seq="rip" type="verbatim">/usr/lib/quagga/ripd -f /etc/quagga/ripd.conf -d</exec>
        <exec seq="ospf" type="verbatim">/usr/lib/quagga/ospfd -f /etc/quagga/ospfd.conf -d -P 2604</exec>
        <exec seq="stop" type="verbatim">hostname</exec>
        <exec seq="stop" type="verbatim">killall zebra</exec>
        <exec seq="stop" type="verbatim">killall ripd</exec>
        <exec seq="stop" type="verbatim">killall ospfd</exec>
 
        <exec seq="rpfilter" type= "verbatim">
            for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
        </exec>
    </vm>
 
    <vm name="r2">
        <if id="1" net="net1">
            <ipv4 mask="255.255.255.0">10.0.1.2</ipv4>
        </if>
        <if id="2" net="net2">
            <ipv4 mask="255.255.255.0">10.0.2.1</ipv4>
        </if>
 
        <forwarding type="ipv4" />
 
        <filetree root="/etc/quagga" seq="start">conf</filetree>
        <exec seq="start" type="verbatim">sysctl -w net.ipv4.conf.all.rp_filter=0</exec>
        <exec seq="start" type="verbatim">hostname</exec>
        <exec seq="start" type="verbatim">/usr/lib/quagga/zebra -f /etc/quagga/zebra.conf -d</exec>
        <exec seq="rip" type="verbatim">/usr/lib/quagga/ripd -f /etc/quagga/ripd.conf -d</exec>
        <exec seq="ospf" type="verbatim">/usr/lib/quagga/ospfd -f /etc/quagga/ospfd.conf -d -P 2604</exec>
        <exec seq="stop" type="verbatim">hostname</exec>
        <exec seq="stop" type="verbatim">killall zebra</exec>
        <exec seq="stop" type="verbatim">killall ripd</exec>
        <exec seq="stop" type="verbatim">killall ospfd</exec>
 
        <exec seq="rpfilter" type= "verbatim">
            for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
        </exec>
    </vm>
 
    <vm name="r3">
        <if id="1" net="net2">
            <ipv4 mask="255.255.255.0">10.0.2.2</ipv4>
        </if>
 
        <forwarding type="ipv4" />
 
        <filetree root="/etc/quagga" seq="start">conf</filetree>
        <exec seq="start" type="verbatim">sysctl -w net.ipv4.conf.all.rp_filter=0</exec>
        <exec seq="start" type="verbatim">hostname</exec>
        <exec seq="start" type="verbatim">/usr/lib/quagga/zebra -f /etc/quagga/zebra.conf -d</exec>
        <exec seq="rip" type="verbatim">/usr/lib/quagga/ripd -f /etc/quagga/ripd.conf -d</exec>
        <exec seq="ospf" type="verbatim">/usr/lib/quagga/ospfd -f /etc/quagga/ospfd.conf -d -P 2604</exec>
        <exec seq="stop" type="verbatim">hostname</exec>
        <exec seq="stop" type="verbatim">killall zebra</exec>
        <exec seq="stop" type="verbatim">killall ripd</exec>
        <exec seq="stop" type="verbatim">killall ospfd</exec>
 
        <exec seq="rpfilter" type= "verbatim">
            for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
        </exec>
    </vm>
</vnuml>

Die Topologie des Beispielscenarios mini.xml

Die Topologie des Beispielscenarios mini.xml

Dieses Beispiel besteht aus 3 Routern, die mit zwei Netzen zu einer “Kette” verbunden sind (siehe Abbildung rechts). Startet man dieses Scenario, so kann man nicht von r1 (10.0.1.1) nach r3 (10.0.2.2) pingen, da die Routingdaemons noch nicht gestartet sind und r1 noch nichts von net2 weiß:

sudo vnumlparser.pl -t mini.xml
sudo ssh root@r1
ping 10.0.2.2

Wie man sieht, kommen die Pings nicht bei r3 an. Mit exit verläst man den Router r1 wieder.
Es gibt nun 4 Sequenzen, die ausgeführt werden können: start, stop, rip und ospf. Dabei wird durch ausführen der Sequenz start der Hostname gesetzt und der zebra-Daemon gestartet. Mit rip oder ospf wird der entsprechende Routingdaemon gestartet und stop beendet alle Routingprozesse.
Man startet nun also z.B. den RIP-Daemon mit den folgenden Befehlen:

sudo vnumlparser.pl -x start@mini.xml
sudo vnumlparser.pl -x rip@mini.xml

Nun kann man testen, ob das Routing auch funktioniert, indem man den Ping, der vorher fehlschlug wiederholt:

sudo ssh root@r1
ping 10.0.2.2

Jetzt sollte der Ping funktionierten. Bei größeren Scenarien kann es einige Sekunden dauern bis alle Router über die notwendigen Informationen verfügen (also bis das Scenario konvergent ist).

Natürlich gibt es noch viele weitere Optionen und Möglichkeiten, die VNUML zur Verfügung stellt. Dies soll nur eine kleine Einführung sein, um die ersten Schritte mit diesem System zu machen.

Links


Bloggeramt.de frisch gebloggt Blogverzeichnis - Blog Verzeichnis bloggerei.de