Freitag, 13. März 2009

VI

Mehrere Fenster im vi

Das ist ein Feature was meiner Erfahrung nach sehr stark von der Implementation des vi abhängt - es kann also sein, das einige oder alle der folgenden Kommandos nicht funktionieren. Dann hat man halt Pech gehabt.

Man kann einmal den vi mit dem Schalter -o aufrufen. So öffnet vi -o file1 file2 file3 jedes File in einem eigenen Fenster. Der Bildschirm wird dabei horizontal geteilt, mit vi -O wird der Bildschirm vertikal geteilt. Mit vi -o N kann man N Fenster öffnen.

In einige vi's geht es mit CTRL W S. Sollte dann der Bildschirm eingefroren sein, dann kommt man mit CTRL Q wieder ans Arbeiten.

Zum Schluß gibt es noch das split-Kommando: mit :N split (wobei N die Höhe des Fensters angibt) kann man das aktuelle Fenster in zwei teilen. Vertikal geht das auch: :N vsplit. Will man während man ein File bearbeitet, ein anderes öffnen, dann braucht man die Kommandos new und vnew: :N new file1. Es geht auch im Read-only Modus: sview. Um die offenen Fenster zu wechseln, schließen oder auszublenden dienen die Befehle quit, close, hide und only.



Die Textpuffer

Es gibt im vi genau 36 Textpuffer, die der User nutzen kann. Man unterscheidet zwischen statischen Puffern, von denen 26 möglich sind, und dynamischen Puffern, von denen es 10 gibt. Die Puffer des vi haben eine spezielle Eigenschaft: man kann einen Befehl ausschreiben, ihn in einen Puffer kopieren, und dann den Puffer mit dem Klammeraffen ("@") aufrufen - der Befehl wird ausgeführt. Das ist eigentlich nicht von praktischen Nutzen, jedoch gut zu wissen.

Statische Puffer

Diese Puffer werden mit den Buchstaben des Alphabeths benannt und ändern sich nur, wenn der User sie explizit anspricht. Es macht einen Unterschied, ob man den Puffer mit großen oder kleinen Buchstaben benennt: benutzt man Großbuchstaben, so wird der Text an den Inhalt des Puffers angefügt. Mit Kleinbuchstaben wird der Puffer neu überschrieben.

Dynamische Puffer

In diese Puffer schreibt der vi gelöschte Zeilen oder Zeichen. Die Puffer werden mit den Zahlen 1 - 9 benannt. Löscht man etwas, so wird das in den ersten Puffer (der Puffer "0") gelegt. Bei weiterem Löschen wandert der Inhalt in der Hierarchie der Puffer weiter hoch. Wirklich gelöscht ist es erst, wenn der Puffer 9 überschrieben wird.
Je nachdem, wie viel man löscht, kann man die Information solange wieder aus dem Puffer holen, bis sie im hypothetischen Puffer 10 angekommen ist... (das gilt nur für ganze Zeilen).

Laden und Leeren der Puffer

Das Laden geschieht mit den Anführungsstrichen ("). Ein Ladekommando setzt sich immer zusammen aus dem Namen des Puffers und einem Bewegungskommando (delete, yank). Hier eine Reihe Beispiele:
"a5yy kopiert die fünf folgenden Zeilen in den Puffer a.
"A3yy hängt an den Puffer a weitere 3 Zeilen an.
"fy'd kopiert von Cursor bis zur Textmarke d alles in den Puffer f.
Mit so einem Befehl kopiert man immer bis zur Textmarke, die Marke selbst also nicht mehr.

Möchte man die nummerierten Buffer durchsuchen, so geht man folgenderart vor:
Zuerst ruft man den Buffer 1 auf
"1p
Die entsprechende Zeile wird eingefügt. War es das nicht, so entfernt man die Zeile mit
u und wieder holt mit dem ..
Das wird wiederholt, bis die Buffer alle geprüft sind - dann ist der String verloren...
Einzelne Buchstaben oder Wörter werden erst garnicht in die nummerierten Buffer gelegt.

Zum Einfügen werden auch die Anführungsstriche (") benutzt. Im Kommando wird jetzt statt des Bewegungskommandos das Pastekommando (p oder P) gegeben. Das kleine p fügt den Pufferinhalt nach dem Cursor ein, das große P fügt vor dem Cursor ein.



Die Textmarker

Der vi verfügt über 26 Textmarker, die durch die Buchstaben des Alphabets benannt werden. Ein Marker wird mit dem Befehl "m" und der Angabe des Namens gesetzt. Der gewählte Punkt ist dann die Referenz für Bewegungen, Löschen, Kopieren etc.
Beispiele:
ma setzt den Marker a.
Auf diesen kann man sich jetzt bei der Bewegung beziehen:
`a (Akzent) springt genau auf die Marke,
'a (Hochkomma) an den Anfang der Zeile der Marke.



Abkürzungen

Abkürzungen sind sehr einfach und praktisch. Ich will z.B. nicht immer "Dynamic Host Configuration Protocol" ausschreiben. Also setze ich folgenden Befehl ab:

:abbr dhcp Dynamic Host Configuration Protocol

Wenn ich jetzt "dhcp" (mit Leerzeichen davor und dahinter) tippe, dann expandiert der vi das sofort zum langen Wort. Möchte ich jetzt einmal doch nur die 4 Buchstaben "dhcp" schreiben, dann kann ich entweder "dhcp^V" tippen (also d,h,c,p,CTRL-V, Leertaste) oder ich kann mit dem Kommando "unabbr" die Abkürzung zurücksetzen.
Es ist wichtig, sich zu merken, daß bei Abkürzungen der vi auf das Zeichen nach der eingebenen Sequenz wartet.

Mappen von Tasten

Hierbei wird die definierte Sequenz sofort expandiert, und man kann auch komplizierte Macros auf eine Tastenkombination binden. Dazu dient das Kommando "map". Es gibt zwei Möglichkeiten, die verschiedene Modi des vi benutzten.
map funktioniert mit Kommandosequenzen, die im Kommando Mode (also nach einem ESCAPE) eingegeben werden, während map! auf im Textmodus getippte Sequenzen reagiert.

map! - Text Mode

Die Eingabe des folgenden Befehls bringt den vi dazu, die Folge "dhcp" nicht mehr auf den Bildschirm zu bringen, sondern direkt zu ersetzen:

:map! dhcp Dynamic Host Configuration Protocol

Der "map!"-Befehl kann wie gehabt mit "unmap!" wieder aufgehoben werden und mit CTRL-V temporär umgangen werden. Man kann alle aktuellen Mappings durch eingeben von "map!" am Doppelpunkt sehen.

map - Kommando Mode

Wie im vorigen Abschnitt, nur das die definierte Sequenz im Kommando-Mode, also nach einem ESCAPE, benutzt werden muß. Der "map"-Befehl kann wie gehabt mit "unmap" wieder aufgehoben werden und mit CTRL-V temporär umgangen werden. Man kann alle aktuellen Mappings durch eingeben von "map" am Doppelpunkt sehen.

Das .exrc-File

Abkürzungen und Mappings kann man dem VI beim starten mitgeben. Es ist auch möglich, eine ganze Reihe anderer Steuerbefehle mitzugeben - und man kann pro Unterverzeichnis ein spezielles .exrc pflegen (z.B. für HTML und LaTex). So kann man verschiedene, spezialisierte VI-Instanzen laufen lassen. Über die Umgebungsvariable EXINIT kann bestimmt werden, wie der VI starten soll. Es ist auch jederzeit möglich, ein vorhandenes .exrc einzulesen:
:so .xxxx

Hier ein Beispiel .exrc:

" Ein Kommentar
set noautoindent
set showmode
set tabstop=8
...
ab c <>
ab _c < /CENTER>
ab hf < href="">
...
" Ein Macro:
" CTRL X Nummer: H1 bis H2 (1-2)
map ^X1 I<>^A< /H1> ^[D^[o
map ^X2 I<>^A< /H2> ^[D^[o
...
Die Syntax ist eigentlich selbsterklärend, und zu den Macros kommen wir jetzt.

Macros

Das schreiben von Macros ist eigentlich sehr einfach, da es sich um Abfolgen von vi-Befehlen handelt, die man normalerweise auch so Absetzen kann. Aufgrund der Struktur der Buffer ist es innerhalb eines Macros zusätzlich möglich, Dinge zeitlich zu versetzen, also eine Eingabe zu machen, die erst nach der Eingabe an die richtige Stelle kopiert wird.

Die große Gefahr sind recursive macros, also Macros, die sich selbst während ihrer Ausführung erneut aufrufen. Ein Beispiel wäre folgendes Mapping: map! dhcp Das dhcp klasse sein. Hier ruft das Macro sich selbst auf und es kommt zur Katatrophe. Daher ist man gut beraten, wenn man sich seine Macros vorher gut anschaut und gründlich testet, bevor man sie eingibt oder im .exrc verewigt.

Hier einige nützliche Beispiele - der Fantasie sind keine Grenzen gesetzt. In den Macros müssen eine Reihe Steuersequenzen eingesetzt werden. Diese werden mit der CTRL-Taste (STRG-Taste) erzeugt. So ist ^X die Kombination CTRL-X etc. Ein spezieller Fall ist die Escape-Sequenz ^[, die man mit der Kombination CTRL-V und dann ESCAPE-Taste erhält. Da es Stuerzeichen sind, ist es nicht möglich, eine solche Zeile z.B. mit der Maus zu kopieren (greppen), da die Steuerzeichen verloren gehen.

map ^X1 I<>^A< /H1> ^[D^[o
Hier ist definiert, das die Tastenkombination CTRL-X auf einer gerade geschriebenen Zeile folgende Aktionen auslöst: Es wird am Anfang der Zeile die Header-1 Anweisung eingefügt, am Ende ausgeführt, bis zum Zeilenende gelöscht und eine neue Zeile zur Eingabe geöffnet.
map ^X^L o< /A>^[kO">^[I< href="">
Die Kombination CTRL-x + w führt dazu, das der unter dem Cursor liegende Text in eine HREF... Anweisung eingeführt wird. Der Cursor springt dann noch zur richtigen Stelle zur Eingabe des Links (URL).
map ^X^W a< /A>^[bbbi< href="http://#">^[F#
Dasselbe für ein Wort (statt der ganzen Zeile).

Man kann Macros verketten - dazu dient der "=" Befehl. Damit ist es möglich, einige ärgerliche Begrenzungen bezüglich der Länge eines Macros zu überwinden.


Einfaches umbennen vieler Files

Der vi ist auch sehr praktisch beim Bearbeiten großer Mengen an Files, z.B. wenn man 1000 Files "name.txt" nach " name.html" umbenennen möchte:

Man öffnet den vi und geht in den Colon Mode:


:r !ls *.txt # xyz.txt
:%s/.*/mv & &/ # mv xyz.txt xyz.txt
:%s/txt$/html/ # mv xyz.txt xyz.html
:w !sh
:q!

In der ersten Anweisung liest der vi ein Listing des Inhalts des Direktories ein. Das kann man natürlich auch noch editieren. Die zweite Anweisung sagt dem vi, den ganzen Ausdruck (.*) durch "mv & &" zu ersetzen. Der Ampersand steht dabei für den Ausdruck, der mit ".*" in den Buffer geschrieben wurde. Jetzt wird "txt" durch "html" ersetzt, und zwar nur, wenn es am Ende der Zeile steht, daher das "$". Man kann nun die Sache nochmal checken. Mit der Write-Anweisung schreibt man das Ergebnis nicht in ein File, sondern auf eine Shell, die jede Zeile als Kommando interpretiert und ausführt.

Das sieht im Beispiel so aus:
Im VI, nach einlesen mit ":r !ls *":

plautze1.jpg
plautze2.jpg
plautze3.jpg
plautze4.jpg
plautze5.jpg
Hier der Befehl, der folgende Zeile erzeugt:
Das ist das File plautze*.jpg ...
:.,5s/.*/Das ist das File & .../

Jetzt hat man oft das Problem, daß man am Filenamen etwas ändern möchte, was nicht am Ende steht. Zb. sollen alle Files sapPEUvg3.out nach sapGAGvg3.out umbenannt werden. Man erzeugt also wie oben folgende vi-Datei:

mv sapPEUvg0.out sapPEUvg0.out
mv sapPEUvg1.out sapPEUvg1.out
mv sapPEUvg2.out sapPEUvg2.out
mv sapPEUvg3.out sapPEUvg3.out
Jetzt muß man das zweite PEU jeder Zeile in GAG umschreiben; das macht einen Trick nötig, da vi die Zeilen von links nach rechts durchgeht. Daher kann man entweder jeweils die erste Instanz ändern, oder alle. Man ändert also alle PEU in einer Zeile in GAG, und ändert dann das erste GAG in PEU zurück:
:%s/PEU/GAG/g  ==> alle Namen haben jetzt das GAG.
:%s/GAG/PEU/ ==> nur das jeweils erste GAG auf der Zeile wird zum PEU.
Man kann auch richtig professionel vorgehen und zuerst das erste PEU in GAG umbenennen und dann die Worte tauschen:
g/GAG/s/^\([^ ]*\) \([^ ]*\) \([^ ]*\)/\1 \3 \2/
Das vertauscht auf jeder Zeile, die "GAG" enthält, das zweite mit dem dritten Wort; man erhält das gewünschte File und kann die Files umbennen.

Wenn man weiss, was man tut, dann kann man sich den Umweg über den vi sparen:

ls -d *.xxx | sed "s/.*\)\.xxx$/mv '&# '\1.old'/" | sh

Macht aus File bla.xxx das File bla.old.

ls -d | sed "s/[A-Z,a-z,0-9,.,_,-]*/mv '&' '&.html'/" | sh