Ihr Problem mit Vim ist, dass Sie vi nicht grok .
Sie erwähnen das Schneiden mit yy
und beschweren sich, dass Sie fast nie ganze Linien schneiden wollen. Tatsächlich möchten Programmierer, die Quellcode bearbeiten, sehr oft ganze Zeilen, Zeilenbereiche und Codeblöcke bearbeiten. Es yy
ist jedoch nur eine von vielen Möglichkeiten, Text in den anonymen Kopierpuffer zu ziehen (oder "zu registrieren", wie es in vi genannt wird ).
Das "Zen" von vi ist, dass Sie eine Sprache sprechen. Die Initiale y
ist ein Verb. Die Aussage yy
ist ein Synonym für y_
. Das y
wird verdoppelt, um die Eingabe zu vereinfachen, da es sich um eine so übliche Operation handelt.
Dies kann auch ausgedrückt werden als dd
P
(Löschen der aktuellen Zeile und Einfügen einer Kopie an die richtige Stelle; Belassen einer Kopie im anonymen Register als Nebeneffekt). Die y
und d
"Verben" nehmen jede Bewegung als ihr "Subjekt". Somit yW
ist "von hier (der Cursor) bis zum Ende des aktuellen / nächsten (großen) Wortes ziehen" und y'a
"von hier bis zur Zeile mit der Markierung" a "ziehen".
Wenn Sie nur grundlegende Cursorbewegungen nach oben, unten, links und rechts verstehen, ist vi für Sie nicht produktiver als eine Kopie von "notepad". (Okay, Sie haben immer noch Syntaxhervorhebung und die Möglichkeit, Dateien zu verarbeiten, die größer als etwa 45 KB sind; aber arbeiten Sie hier mit mir zusammen).
vi hat 26 "Markierungen" und 26 "Register". Mit dem m
Befehl wird eine Markierung auf eine beliebige Cursorposition gesetzt . Jede Marke ist durch einen einzelnen Kleinbuchstaben gekennzeichnet. Somit ma
setzt die ‚ eine ‘ Markierung der aktuellen Position und mz
setzt die ‚ z ‘ Zeichen. Mit dem '
Befehl (in einfachen Anführungszeichen) können Sie zu der Zeile mit einer Markierung wechseln . Bewegt 'a
sich also an den Anfang der Zeile mit der Markierung ' a '. Mit dem `
Befehl (backquote) können Sie zur genauen Position einer beliebigen Markierung wechseln . Dadurch `z
wird direkt an die genaue Position der ' z' -Markierung verschoben .
Da es sich um "Bewegungen" handelt, können sie auch als Subjekte für andere "Aussagen" verwendet werden.
Eine Möglichkeit, eine beliebige Textauswahl auszuschneiden, besteht darin, eine Markierung zu löschen (normalerweise verwende ich " a " als "erste" Markierung, " z " als nächste Markierung, " b " als andere und " e " als noch eine (ich erinnere mich nicht, dass ich in 15 Jahren mit vi jemals mehr als vier Markierungen interaktiv verwendet habe ; man erstellt seine eigenen Konventionen darüber, wie Markierungen und Register von Makros verwendet werden, die den interaktiven Kontext nicht stören). Dann geht es los zum anderen Ende unseres gewünschten Textes, wir können an beiden Enden beginnen, es spielt keine Rolle. Dann können wir einfach d`a
zum Ausschneiden oder y`a
Kopieren verwenden. Somit hat der gesamte Prozess 5 Tastenanschläge (sechs, wenn wir mit "Einfügen" begonnen haben ""Modus und benötigtEscout befehlsmodus). Sobald wir ausgeschnitten oder kopiert haben, ist das Einfügen einer Kopie ein einziger Tastendruck : p
.
Ich sage, dass dies eine Möglichkeit ist, Text auszuschneiden oder zu kopieren. Es ist jedoch nur eines von vielen. Häufig können wir den Textbereich prägnanter beschreiben, ohne den Cursor zu bewegen und eine Markierung fallen zu lassen. Zum Beispiel , wenn ich in einem Absatz des Textes bin ich verwenden kann , {
und }
jeweils an den Anfang oder das Ende des Absatzes Bewegungen. Um einen Textabschnitt zu verschieben, schneide ich ihn mit {
d}
(3 Tastenanschlägen). (Wenn ich mich gerade in der ersten oder letzten Zeile des Absatzes befinde, kann ich einfach d}
oder d{
verwenden.
Der Begriff "Absatz" verwendet standardmäßig etwas, was normalerweise intuitiv sinnvoll ist. Daher funktioniert es oft sowohl für Code als auch für Prosa.
Häufig kennen wir ein Muster (regulärer Ausdruck), das das eine oder andere Ende des Textes markiert, an dem wir interessiert sind. Vorwärts oder rückwärts suchen sind Bewegungen in vi . Somit können sie auch als "Subjekte" in unseren "Aussagen" verwendet werden. So kann ich d/foo
von der aktuellen Zeile zur nächsten Zeile mit der Zeichenfolge "foo" schneiden und y?bar
von der aktuellen Zeile in die letzte (vorherige) Zeile mit "bar" kopieren. Wenn ich keine ganzen Zeilen möchte, kann ich trotzdem die Suchbewegungen (als eigene Anweisungen) verwenden, meine Markierung (en) löschen und die `x
Befehle wie zuvor beschrieben verwenden.
Neben "Verben" und "Subjekten" hat vi auch "Objekte" (im grammatikalischen Sinne des Wortes). Bisher habe ich nur die Verwendung des anonymen Registers beschrieben. Allerdings kann ich eine der 26 „Namen“ Register verwenden , indem prefixing das „Objekt“ Referenz mit "
(der doppelten Anführungszeichen Modifikator). Also wenn ich verwende "add
ich die aktuelle Zeile in die ‚cutting a ‘ registrieren und wenn ich "by/foo
dann eine Kopie des Textes von hier in die nächste Zeile ich zerren „foo“ in die ‚enthalten , b ‘ registrieren. Um aus einem Register einzufügen, stelle ich dem Einfügen einfach dieselbe Modifikatorsequenz voran: "ap
Fügt eine Kopie des Registers ' a ' ein."bP
Fügt eine Kopie von ' b ' bis vor die aktuelle Zeile ein.
Dieser Begriff der "Präfixe" fügt unserer Sprache der Textmanipulation auch die Analoga der grammatikalischen "Adjektive" und "Adverbien" hinzu. Die meisten Befehle (Verben) und Bewegungen (Verben oder Objekte, je nach Kontext) können auch numerische Präfixe annehmen 3J
bedeutet "die nächsten drei Zeilen verbinden" und d5}
"aus der aktuellen Zeile bis zum Ende des fünften Absatzes von hier unten löschen".
Dies ist alles Zwischenstufe vi . Nichts davon ist Vim- spezifisch und es gibt weitaus fortgeschrittenere Tricks in vi, wenn Sie bereit sind, sie zu lernen. Wenn Sie nur diese Zwischenkonzepte beherrschen, werden Sie wahrscheinlich feststellen, dass Sie selten Makros schreiben müssen, da die Textbearbeitungssprache ausreichend präzise und ausdrucksstark ist, um die meisten Dinge mit der "Muttersprache" des Editors leicht genug zu erledigen.
Eine Auswahl fortgeschrittener Tricks:
Es gibt eine Reihe von :
Befehlen, insbesondere die :% s/foo/bar/g
globale Substitutionstechnik. (Das ist nicht fortgeschritten, aber andere :
Befehle können sein). Der gesamte :
Befehlssatz wurde historisch von den früheren Inkarnationen von vi als Dienstprogramme ed (Zeileneditor) und später ex (erweiterter Zeileneditor) geerbt. Tatsächlich heißt vi so, weil es die visuelle Schnittstelle zu ex ist .
:
Befehle werden normalerweise über Textzeilen ausgeführt. ed und ex wurden in einer Zeit geschrieben, in der Terminalbildschirme ungewöhnlich waren und viele Terminals "Telety Type" (TTY) -Geräte waren. Daher war es üblich, aus gedruckten Kopien des Textes mit Befehlen über eine extrem knappe Oberfläche zu arbeiten (übliche Verbindungsgeschwindigkeiten waren 110 Baud oder ungefähr 11 Zeichen pro Sekunde - was langsamer ist als bei einem schnellen Schreibkraft; Verzögerungen waren häufig interaktive Sitzungen für mehrere Benutzer (zusätzlich gab es häufig eine gewisse Motivation, Papier zu sparen).
Die Syntax der meisten :
Befehle enthält daher eine Adresse oder einen Adressbereich (Zeilennummer), gefolgt von einem Befehl. Natürlich könnte man wörtliche Zeilennummern verwenden: :127,215 s/foo/bar
um das erste Vorkommen von "foo" in "bar" in jeder Zeile zwischen 127 und 215 zu ändern. Man könnte auch einige Abkürzungen wie .
oder $
für aktuelle bzw. letzte Zeilen verwenden. Man könnte auch relative Präfixe verwenden +
und -
sich auf Offsets nach bzw. vor der aktuellen Linie beziehen. Also: :.,$j
bedeutet "von der aktuellen bis zur letzten Zeile, füge sie alle zu einer Zeile zusammen". :%
ist gleichbedeutend mit :1,$
(allen Zeilen).
Die Befehle :... g
und sind :... v
erklärungsbedürftig, da sie unglaublich mächtig sind. :... g
ist ein Präfix für "global", das einen nachfolgenden Befehl auf alle Zeilen anwendet, die einem Muster entsprechen (regulärer Ausdruck), während ein :... v
solcher Befehl auf alle Zeilen angewendet wird, die NICHT mit dem angegebenen Muster übereinstimmen ("v" von "conVerse"). Wie bei anderen ex- Befehlen können diesen Adress- / Bereichsreferenzen vorangestellt werden. Dies :.,+21g/foo/d
bedeutet "alle Zeilen löschen, die die Zeichenfolge" foo "enthalten, von der aktuellen bis zu den nächsten 21 Zeilen", während :.,$v/bar/d
"von hier bis zum Ende der Datei alle Zeilen löschen, die die Zeichenfolge" bar "NICHT enthalten.
Es ist interessant, dass der allgemeine Unix-Befehl grep tatsächlich von diesem Ex- Befehl inspiriert wurde (und nach der Art und Weise benannt ist, in der er dokumentiert wurde). Mit dem Befehl ex:g/re/p
(grep) wurde dokumentiert, wie Zeilen mit einem "regulären Ausdruck" (re) "global" "gedruckt" werden. Wenn ed und ex verwendet wurden, war der :p
Befehl einer der ersten, die jemand gelernt hat, und oft der erste, der beim Bearbeiten einer Datei verwendet wurde. Auf diese Weise haben Sie den aktuellen Inhalt gedruckt (normalerweise nur jeweils eine Seite mit :.,+25p
oder einem ähnlichen Inhalt ).
Beachten Sie, dass :% g/.../d
oder (sein Gegenstück zu reVerse / conVerse: :% v/.../d
die häufigsten Verwendungsmuster sind. Es gibt jedoch einige andere ex
Befehle, an die Sie sich erinnern sollten:
Wir können m
Linien verschieben und j
Linien verbinden. Wenn Sie beispielsweise eine Liste haben und alle übereinstimmenden (oder umgekehrt NICHT mit einem Muster übereinstimmenden) Elemente trennen möchten, ohne sie zu löschen, können Sie Folgendes verwenden: :% g/foo/m$
... und alle "foo" -Zeilen wurden verschoben das Ende der Datei. (Beachten Sie den anderen Tipp zur Verwendung des Endes Ihrer Datei als Arbeitsbereich). Dadurch bleibt die relative Reihenfolge aller "foo" -Zeilen erhalten, während sie aus dem Rest der Liste extrahiert wurden. (Dies wäre gleichbedeutend mit: 1G!GGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d
(Kopieren Sie die Datei in ihren eigenen Schwanz, filtern Sie den Schwanz durch grep
und löschen Sie alle Inhalte aus dem Kopf).
Um Linien normalerweise zu verbinden, kann ich ein Muster für alle Linien finden, die mit ihrem Vorgänger verbunden werden müssen (zum Beispiel alle Linien, die in einer Aufzählungsliste mit "^" anstelle von "^ *" beginnen). In diesem Fall würde ich Folgendes verwenden: :% g/^ /-1j
(Gehen Sie für jede übereinstimmende Zeile eine Zeile nach oben und verbinden Sie sie). (Übrigens: Bei Aufzählungslisten funktioniert der Versuch, nach Aufzählungslinien zu suchen und mit der nächsten zu verbinden, aus mehreren Gründen nicht. Es kann eine Aufzählungslinie mit einer anderen verbinden und es wird keine Aufzählungslinie mit allen verknüpft seine Fortsetzung; es wird nur paarweise bei den Spielen funktionieren).
Fast unnötig zu erwähnen, dass Sie unseren alten Freund s
(Ersatz) mit den Befehlen g
und v
(global / converse-global) verwenden können. Normalerweise müssen Sie dies nicht tun. Stellen Sie sich jedoch einen Fall vor, in dem Sie eine Ersetzung nur für Zeilen durchführen möchten, die mit einem anderen Muster übereinstimmen. Oft können Sie ein kompliziertes Muster mit Captures verwenden und Rückverweise verwenden, um die Teile der Linien beizubehalten, die Sie NICHT ändern möchten. Es ist jedoch oft einfacher, die Übereinstimmung von der Ersetzung zu trennen: :% g/foo/s/bar/zzz/g
- Ersetzen Sie für jede Zeile, die "foo" enthält, alle "Balken" durch "zzz". (Etwas wie:% s/\(.*foo.*\)bar\(.*\)/\1zzz\2/g
würde nur für die Fälle funktionieren, in denen "bar" in derselben Zeile von "foo" vorangestellt wurde; es ist schon unansehnlich genug und müsste weiter verstümmelt werden, um alle Fälle zu erfassen, in denen "bar" vor "foo" stand)
Der Punkt ist , dass es mehr als nur p
, s
und d
Linien in dem ex
Befehlssatz.
Die :
Adressen können sich auch auf Marken beziehen. Sie können also :'a,'bg/foo/j
Folgendes verwenden , um eine beliebige Zeile, die den String foo enthält, mit der nachfolgenden Zeile zu verbinden, wenn sie zwischen den Zeilen zwischen den Markierungen ' a ' und ' b ' liegt. (Ja, alle vorhergehenden ex
Befehlsbeispiele können auf Teilmengen der Dateizeilen beschränkt werden, indem diese Art von Adressierungsausdrücken vorangestellt wird.)
Das ist ziemlich dunkel (ich habe so etwas in den letzten 15 Jahren nur ein paar Mal benutzt). Ich gebe jedoch frei zu, dass ich oft iterativ und interaktiv Dinge getan habe, die wahrscheinlich effizienter hätten erledigt werden können, wenn ich mir die Zeit genommen hätte, die richtige Beschwörung auszudenken.
Ein weiterer sehr nützlicher Befehl vi oder ex ist :r
das Einlesen des Inhalts einer anderen Datei. Also: :r foo
fügt den Inhalt der Datei mit dem Namen "foo" in die aktuelle Zeile ein.
Stärker ist der :r!
Befehl. Dies liest die Ergebnisse eines Befehls. Dies entspricht dem Unterbrechen der vi- Sitzung, dem Ausführen eines Befehls, dem Umleiten der Ausgabe in eine temporäre Datei, dem Fortsetzen der vi- Sitzung und dem Einlesen des Inhalts aus der temporären Datei . Datei.
Noch mächtiger sind die Befehle !
(bang) und :... !
( ex bang). Diese führen auch externe Befehle aus und lesen die Ergebnisse in den aktuellen Text ein. Sie filtern jedoch auch eine Auswahl unseres Textes über den Befehl! Auf diese Weise können wir alle Zeilen in unserer Datei mit sortieren 1G!Gsort
( G
ist der Befehl vi "goto"; standardmäßig wird zur letzten Zeile der Datei gewechselt, es kann jedoch eine Zeilennummer vorangestellt werden, z. B. 1, die erste Zeile). Dies entspricht der Ex- Variante :1,$!sort
. Autoren verwenden häufig !
die Unix- Dienstprogramme fmt oder fold , um Textauswahlen neu zu formatieren oder in Zeilenumbrüche umzuwandeln . Ein sehr verbreitetes Makro ist{!}fmt
(Formatieren Sie den aktuellen Absatz neu). Programmierer verwenden es manchmal, um ihren Code oder nur Teile davon durch Einrückungen oder andere Tools zur Neuformatierung des Codes auszuführen.
Die Verwendung der Befehle :r!
und !
bedeutet, dass jedes externe Dienstprogramm oder jeder externe Filter als Erweiterung unseres Editors behandelt werden kann. Ich habe diese gelegentlich mit Skripten verwendet, die Daten aus einer Datenbank abgerufen haben , oder mit wget- oder lynx- Befehlen, mit denen Daten von einer Website abgerufen wurden , oder mit ssh- Befehlen, mit denen Daten von Remote-Systemen abgerufen wurden.
Ein weiterer nützlicher ex- Befehl ist :so
(kurz für :source
). Dies liest den Inhalt einer Datei als eine Reihe von Befehlen. Wenn Sie vi starten , führt es implizit eine :source
On- ~/.exinitrc
Datei aus (und Vim führt dies normalerweise ~/.vimrc
natürlich ein). Dies dient dazu, dass Sie Ihr Editorprofil im laufenden Betrieb ändern können, indem Sie einfach neue Makros, Abkürzungen und Editoreinstellungen eingeben. Wenn Sie hinterhältig sind, können Sie dies sogar als Trick zum Speichern von Sequenzen von Ex- Bearbeitungsbefehlen verwenden, die bei Bedarf auf Dateien angewendet werden.
Zum Beispiel habe ich eine siebenzeilige Datei (36 Zeichen), die eine Datei über wc ausführt und oben in der Datei, die diese Wortzählungsdaten enthält , einen Kommentar im C-Stil einfügt. Ich kann dieses "Makro" mit einem Befehl wie dem folgenden auf eine Datei anwenden:vim +'so mymacro.ex' ./mytarget
(Die +
Befehlszeilenoption für vi und Vim wird normalerweise verwendet, um die Bearbeitungssitzung bei einer bestimmten Zeilennummer zu starten. Es ist jedoch wenig bekannt, dass man +
jedem gültigen ex- Befehl / Ausdruck folgen kann , z. B. einem "Quell" -Befehl als Ich habe es hier getan: Für ein einfaches Beispiel habe ich Skripte, die Folgendes aufrufen: vi +'/foo/d|wq!' ~/.ssh/known_hosts
um einen Eintrag aus meiner SSH-Datei für bekannte Hosts nicht interaktiv zu entfernen, während ich eine Reihe von Servern neu abbilde).
Normalerweise ist es viel einfacher, solche "Makros" mit Perl, AWK, sed zu schreiben (was in der Tat wie grep ein Dienstprogramm ist, das vom Befehl ed inspiriert ist ).
Der @
Befehl ist wahrscheinlich der dunkelste vi- Befehl. Ich habe fast ein Jahrzehnt lang gelegentlich Kurse für fortgeschrittene Systemadministration unterrichtet und nur sehr wenige Leute getroffen, die es jemals benutzt haben. @
führt den Inhalt eines Registers so aus, als wäre es ein vi- oder ex- Befehl.
Beispiel: Ich verwende häufig:, :r!locate ...
um eine Datei auf meinem System zu finden und ihren Namen in mein Dokument einzulesen. Von dort aus lösche ich alle überflüssigen Treffer und lasse nur den vollständigen Pfad zu der Datei, an der ich interessiert bin. Anstatt mühsam Tabdurch jede Komponente des Pfads zu blättern (oder schlimmer noch, wenn ich zufällig auf einem Computer ohne Unterstützung für die Tab-Vervollständigung stecke in seiner Kopie von vi ) benutze ich nur:
0i:r
(um die aktuelle Zeile in einen gültigen : r- Befehl umzuwandeln ),
"cdd
(um die Zeile in das "c" -Register zu löschen) und
@c
Führen Sie diesen Befehl aus.
Das sind nur 10 Tastenanschläge (und der Ausdruck "cdd
@c
ist für mich praktisch ein Fingermakro, sodass ich ihn fast so schnell eingeben kann wie jedes übliche Wort aus sechs Buchstaben).
Ein ernüchternder Gedanke
Ich habe nur an die Oberfläche von vi gekratzt und nichts von dem, was ich hier beschrieben habe, ist auch nur ein Teil der "Verbesserungen", nach denen vim benannt ist! Alles, was ich hier beschrieben habe, sollte auf jeder alten Kopie von vi von vor 20 oder 30 Jahren funktionieren .
Es gibt Leute, die wesentlich mehr von vi 's Macht verbraucht haben als ich jemals.