Wie kehre ich die Reihenfolge der Zeilen um?


24

Wie kann ich die Reihenfolge der Zeilen umkehren, sodass die erste Zeile am Ende und die letzte Zeile zuerst angezeigt wird? (Dies können alle Zeilen in einem Puffer, ein Adressbereich oder eine zeilenweise Auswahl des visuellen Modus sein.)

Ich möchte mich verwandeln

rat
ox
tiger
⋮
dog
pig

in

pig
dog
⋮
tiger
ox
rat

ohne auf einen externen Befehl wie tac.


Irgendwelche Vorschläge für bessere Tags zu dieser Frage?
200_success

1
vielleicht ein neues 'pure-vi' oder ähnliches tag? Ich habe mehrere Fragen gesehen, die von einem Tag profitieren würden, der darauf hindeutet, dass keine externen Tools beteiligt sein sollen. Soll ich bei Meta danach fragen?
John O'M.

1
@Carpetsmoker (und alle anderen, die daran interessiert sind, diesem zu folgen) Die Tag-Frage befindet sich jetzt auf meta meta.vi.stackexchange.com/questions/1229/…
John O'M.

Antworten:


29

Die Kraft des Globalen wird hier wirken:

:g/^/exe "normal ddggP"

Oder einfacher (danke @tommcdo)

:g/^/move 0

Die erste Zeile entspricht jeder Zeile. Löschen Sie sie für jede Zeile und fügen Sie sie oben in die Datei ein. Beim Durchlaufen der Datei wird der Text umgekehrt.

Die zweite Zeile stimmt auf ähnliche Weise mit jeder Zeile überein und verschiebt sie an den Anfang der Datei.

Hinweis: Diese beiden Methoden wirken sich auf die gesamte Datei aus und gelten nicht ordnungsgemäß für das Umkehren einer Teilmenge der Zeilen. Siehe Ingo Karkats Antwort für eine Lösung, die in einem bestimmten Bereich funktioniert.

Beschreibung:

gglobaler Befehl
/^/jede Zeile übereinstimmen , die einen Anfang hat (dh alle Leitungen)
exeführt die folgenden Zeichenfolge
"normalauszuführen normaler Modus befiehlt
ddZeile löscht
ggBewegung zu Anfang der Datei
PPaste über aktuelle Position

move 0 Verschiebt die aktuelle Zeile unter Zeile 0 (wodurch sie an Position 1 oder in die erste Zeile der Datei verschoben wird).


6
Anstelle des :normalBefehls können wir den Ex-Befehl verwenden :move 0, der die Zeile an den Anfang des Puffers verschiebt.
tommcdo

1
Auch :executeist nur notwendig, wenn der Befehl dynamisch aufgebaut werden muss, z :execute 'normal' g:user_command.
tommcdo

@tommcdo gute Punkte! Ich habe die Angewohnheit, zu verwenden, :executeweil ich später oft andere Ex-Befehle nach dem vorhandenen anhänge, und es ist für mich bequemer, das :exebereits zu haben, als es später zurückgehen und einfügen zu müssen. Leider ist diese Gewohnheit in diese Antwort eingeflossen, in der sie nicht so häufig vorkommt.
John O'M.

1
Weitere Erläuterungen zu meiner Verwendung von :execute: Da es sich um eine Zeichenfolge handelt, wird klar umrissen, wo die Befehle im Normalmodus enden, obwohl ich die Zeichenfolge nicht konstruiere. Es ist für mich einfacher, ausgeglichene Anführungszeichen zu finden, als danach zu suchen <esc>oder was auch immer, um den Modus zu beenden. Auch dies ist persönliche Präferenz und Gewohnheit. :-)
John O'M.

3
Dies funktioniert übrigens für einen Bereich: :9,11g/^/move 8... Die letzte Zahl muss der Anfang des Bereichs minus 1 sein (angepasst an Ingos Antwort).
Martin Tournoij

13

Dieser Einzeiler (für Sie ~/.vimrc) definiert einen :ReverseBefehl. Sie können den :globalTeil auch direkt verwenden, aber die Syntax von :move(die die Zeilen iterativ vor dem Beginn des Bereichs verschiebt und dadurch umkehrt) ist nicht leicht zu merken:

:command! -bar -range=% Reverse <line1>,<line2>global/^/m<line1>-1

1
Als ein FYI für Leser sind die <line1>& <line2>erforderlich, damit dies in einem Bereich funktioniert, dh :7,9Reverse(sie sind Merkmale von command, nicht globaloder move). Der einfachere :command! -bar -range=% Reverse :global/^/m 0wird auch funktionieren, aber nur für den gesamten Puffer ...
Martin Tournoij

6

Pure Vim:

:g/^/m0

Erläuterung:

Laut :help multi-repeat, :gund sein Vetter :vArbeit in einer zwei Pass Weise.

Der erste Durchlauf :gmarkiert jede übereinstimmende Zeile {pattern}, während der zweite Durchlauf (anscheinend beginnend am Anfang der Datei und bis zum Ende) den folgenden Vorgang ausführt [cmd]. Die obige Verwendung von :gnutzt die Reihenfolge, in der die Zeilen verarbeitet werden (was wahrscheinlich in Ordnung ist, obwohl wahrscheinlich technisch nicht garantiert).

Es funktioniert, indem zuerst jede Zeile markiert und dann die erste markierte Zeile an den Anfang der Datei, dann die zweite an den Anfang der Datei (über die zuvor verschobene Zeile) und dann die dritte markierte Zeile (erneut über die zuvor verschobene Zeile) verschoben wird Zeile) usw., bis die letzte Zeile in der Datei nach oben verschoben wird, wodurch die Datei effektiv umgekehrt wird.

Beachten Sie, dass :gdieser Befehl nicht funktioniert , wenn Zeilen in einer anderen Reihenfolge als von oben nach unten verarbeitet werden.

Quelle: Alle Linien und Potenzen von g bei vim wikia umkehren.

Einige Beispiele mit externen Befehlen:

  • tac(Teil von GNU coreutils - catumgekehrt):

    :%!tac                                                                                                                                                                                                                                                              
    
  • tail unter BSD / OSX (nicht POSIX-konform):

    :%!tail -r
    

    -r Die Option -r bewirkt, dass die Eingabe zeilenweise in umgekehrter Reihenfolge angezeigt wird.

    Überprüfen Sie: man tarfür weitere Details.

Weitere Ideen finden Sie unter:


2
Ist das nicht :g/^/m0dasselbe wie :g/^/move 0, was ist Johns Antwort?
muru

@muru Ich denke schon, aber dieser ist kürzer (laut vim wikia) und ich habe eine andere Erklärung mit ein paar zusätzlichen Beispielen für die Verwendung von Befehlszeilen hinzugefügt.
Kenorb

Ja, ich habe wegen der anderen Befehle upvoted (ich bin auch gekommen, um zu posten tac). Aber ich vermute, die Ablehnung lag daran, dass die Antwort wiederholt wurde.
muru

Ich bin mir bewusst, dass dies tacvon OP erwähnt wurde, aber alle anderen ähnlichen Fragen würden sowieso doppelt vorkommen, daher ist es gut, es noch einmal zu erwähnen. John hat dieses Cmd von @tommcdo comment übernommen. Ich habe es ursprünglich von DerMike übernommen , aber ich glaube, er hat es einfach von wikia übernommen, also habe ich vim wikia Credits gegeben, damit es nicht vollständig dupliziert wird, da die Erklärung völlig anders ist.
Kenorb

Es erhöht den Wert, da es sich um eine viel kürzere Version mit angemessener Erklärung handelt und ich auch die richtigen Quellen nenne. Die Verwendung von Shell-Befehlen ist sehr einfach und bequem. Wenn die Leute nicht einverstanden sind, können sie einfach abstimmen, keine große Sache.
Kenorb

6

Im Sinne von funktionalem VimL:

:call setline(1, reverse(getline(1, line('$'))))
  • getline(1, line('$'))gibt eine Liste aller Zeilen im Puffer zurück. '$'ist ein spezielles Argument, line()das die letzte Zeile im Puffer angibt.
  • reverse(...)Kehrt die Eingabeliste vor Ort um. Man müsste verwenden, reverse(copy(...))wenn die Eingabeliste nicht geändert werden soll.
  • setline(1, ...)Ersetzt die angegebene Zeile durch das zweite Argument. Wenn das zweite Argument eine Liste ist, wird die gleiche Anzahl von Zeilen wie die Länge der Liste durch den Inhalt der Liste ersetzt.

Wenn Sie möchten, können Sie auch einen Befehl definieren, der einen Bereich einnimmt (Standard- %Gesamtpuffer).

:command! -bar -range=% Reverse call setline(<line1>, reverse(getline(<line1>, <line2>)))

1
Ich mag diese Antwort. Es hebt auch nichts hervor (wenn hlsearchaktiviert), wie der :g/Befehl aus den anderen Antworten ... Ist die Leistung vielleicht schlechter? Da getline(1, line('$'))bekommt man den gesamten Puffer im Speicher. reverse()scheint vorhanden zu sein, so dass als solches nur sehr wenig Speicher benötigt wird ...
Martin Tournoij

3

Gemäß der Vim-Dokumentation usr_12.txt - Clevere Tricks

12.4 Zeilenreihenfolge umkehren

Der :globalBefehl kann mit dem :moveBefehl kombiniert werden , um alle Zeilen vor der ersten Zeile zu verschieben, was zu einer umgekehrten Datei führt. Der Befehl lautet:

:global/^/m 0

Abgekürzt:

:g/^/m 0

Der ^reguläre Ausdruck entspricht dem Zeilenanfang (auch wenn die Zeile leer ist). Der :moveBefehl verschiebt die übereinstimmende Zeile nach der mythischen Null-Zeile, sodass die aktuelle übereinstimmende Zeile zur ersten Zeile der Datei wird. Da der :globalBefehl nicht durch die sich ändernde Zeilennummerierung verwechselt wird, werden :globalalle verbleibenden Zeilen der Datei abgeglichen und jeweils als erste abgelegt.

Dies funktioniert auch bei einer Reihe von Linien. Bewegen Sie sich zuerst über die erste Zeile und markieren Sie sie mit mt. Bewegen Sie dann den Cursor zur letzten Zeile im Bereich und geben Sie Folgendes ein:

:'t+1,.g/^/m 't

1

Relative Zahlen verwenden. Der Absatz beginnt in Zeile 13 und spammt weitere 4 Zeilen

 :13,13+4g/^/m12
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.