Vim: Wie lösche ich jede zweite Zeile?

98

Wie es in Vim geht:

VOR:

aaa
bbb
ccc
ddd
eee
fff

NACH DEM:

aaa
ccc
eee
brazh
quelle
Sie sollten in der Lage sein, ein Makro zu verwenden: oreillynet.com/mac/blog/2006/07/…
Mark Wilkins
Ich mag die Fülle an Antworten auf diese Frage. Bevorzugung.
Froggythefrog

Antworten:

105

Sie können hierfür ein Makro verwenden. Mach Folgendes.

  • Starten Sie im Befehlsmodus.
  • Gehen Sie zum Anfang der Datei, indem Sie drücken gg.
  • Drücken Sie qq.
  • Klicken Sie auf den Pfeil nach unten und drücken Sie dddanach.
  • Drücken Sie q.
  • Drücken Sie 10000@q

PS: Um in den Befehlsmodus zu wechseln, drücken Sie einfach einige Male die Esc-Taste.

rui
quelle
11
Angenommen, Ihre Datei ist natürlich weniger als 20.000 Zeilen lang :-)
paxdiablo
3
Eigentlich liegst du falsch Michael. Dies funktioniert einwandfrei, da VI das Makro nicht mehr ausführt, sobald es das Dateiende erreicht. Sie können es sehr einfach mit dem obigen Beispiel ausprobieren.
Rui
1
Nun, wie bei jedem Makro müssen Sie es beim ersten Mal richtig machen, aber danach können Sie es auf jede Datei anwenden, indem Sie einfach 10000 @ q eingeben. So oder so glaube ich, dass es nur eine Frage des Geschmacks ist, also werde ich definitiv keine philosophische Debatte beginnen. :)
Rui
3
Ich bevorzuge 'Makro'. Mit dem Makro beschreiben Sie, wie Sie eine bestimmte Aufgabe lösen würden, und lassen sie von VIM für Sie wiederholen. RUI, ich würde lieber 'j' anstelle der Pfeiltaste verwenden.
SolutionYogi
4
:g/^/+d vom Benutzer stackoverflow.com/users/254635/ib ( unten ) ist eine elegantere Möglichkeit, dies zu lösen.
SergioAraujo
254

Eine elegante (und effiziente) Möglichkeit, die Aufgabe auszuführen, besteht darin, den :+deleteBefehl aufzurufen und die Zeile nach der aktuellen Zeile in jeder Zeile mit dem folgenden Befehl zu entfernen :global:

:g/^/+d
ib.
quelle
10
Warum hat diese Antwort nicht mehr Stimmen bekommen? Das ist so sauber und einfach. Alternativ können Sie die erste Zeile löschen und den obigen Befehl ausführen, um alle ungeraden Zeilen zu löschen.
Usagi
3
@Usagi: Danke! Mir geht es genauso. Und ja, Sie haben absolut Recht, :1d|g/^/+dlöscht die ungeraden Zeilen.
ib.
@Usagi Die andere Antwort wurde 2 Jahre vor Erscheinen seiner Antwort als "beste Antwort" ausgewählt. Auch wenn dies eine elegantere Lösung ist, hätte sie nützlicher geschrieben werden können (dh es wird nicht erwähnt, was ^ tut, während das Makro für jedermann leicht zu verstehen ist).
user1271772
3
Entschuldigung, aber ohne eine Erklärung, wie dieser Befehl das gewünschte Verhalten ausführt, würde ich ihn nicht positiv bewerten und stattdessen die akzeptierte Antwort bevorzugen, da ich verstehe, wie er das Ziel erreicht. Es ist der Unterschied, einem Mann einen Fisch zu geben und ihm das Fischen beizubringen.
Gabe
3
Ich weiß nicht, diese Antwort hat mich dazu gebracht, mit dem Befehl: g zu fischen (das heißt, ich habe diese Seite googelt - vim.wikia.com/wiki/Power_of_g ).
Snetch
67

Wir können gegebene Befehle im normalen Modus verwenden :normaloder :normausführen. Quelle.

:%norm jdd
Tadhg
quelle
15
Diese Antwort erhält nicht genügend Aufmerksamkeit, möglicherweise aufgrund der fehlenden Erklärung sowohl hier als auch auf dem verknüpften, chaotischen Spickzettel. Der Befehl norm führt die Befehle im normalen Modus aus, die den darauf folgenden Buchstaben entsprechen. Hier wird also j ausgeführt, um eine Zeile nach unten zu verschieben, und dd, um sie zu löschen. Dies ist auf die X. Zeile verallgemeinerbar, indem in diesem Fall weitere 'j' hinzugefügt werden.
Imoatama
5
und das% ist eine Abkürzung für 1, $ (führen Sie den folgenden Befehl von Zeile 1 bis zum Ende aus)
Lambart
3
Ich bin daran interessiert, warum %norm ddjes nicht funktioniert, wenn Sie ungerade Zeilen löschen möchten.
Cyker
10
:map ^o ddj^o
^o

Hier steht ^ für STRG. Rekursives Makro zum Löschen einer Zeile alle zwei Zeilen. Wählen Sie gut Ihre erste Zeile und es ist geschafft.

Raoul Supercopter
quelle
8

aus dem vim mail archiv :

:let i=1 | while i <= line('$') | if (i % 2) | exe i . "delete" | endif | let i += 1 | endwhile

(Wenn Sie in einer Zeile der vim-Befehlszeile eingeben, wird die Zeile 1,3,5,7, ... gelöscht.)

Fortega
quelle
4
Dann würde ich es vorziehen !perl -ne 'print unless $. % 2':-)
Alex Brasetvik
Und, Alex, ich würde meine Lösung definitiv Ihrer vorziehen, aber diese ist eine Überlegung wert, da dies die einzig sinnvolle Lösung ist, bei der nur vim selbst verwendet wird.
Michael Krelin - Hacker
Der Makroweg ist so viel einfacher.
trusktr
+1 wegen der Flexibilität, mit if (expression)der auch für jede dritte Zeile usw. funktioniert
Jens
5

Sie können jederzeit einen Shell-Befehl durchlaufen, dh Sie können eine beliebige Skriptsprache verwenden:

:%!perl -nle 'print if $. % 2'

(oder verwenden Sie "es sei denn" anstelle von "wenn", je nachdem, welche Zeilen Sie möchten)

schlank
quelle
Sie brauchen den -lWechsel zu Perl nicht ... außerdem würde ich sagen, dass dies awketwas weiter verbreitet ist als Perl, obwohl es heutzutage nicht mehr so ​​viel ist und dies kürzer machen kann: :%!awk NR\%2oder :%!awk 1-NR\%2für die anderen Zeilen.
Ephemient
4
:%!awk -- '++c\%2'

Alternative

:%!awk -- 'c++\%2'

je nachdem welche hälfte du aussortieren willst.

Michael Krelin - Hacker
quelle
Umm, warum nicht die vorhandene NRVariable verwenden?
Ephemient
Richtig. Weil ich es vergessen habe. ;-) Aber tatsächlich wird die "richtige" Variante mit NR noch länger sein. Und es würde keine zwei gut aussehenden Gegenstücke geben.
Michael Krelin - Hacker
:%!awk NR\%2oder :%!awk 1-NR\%2, wie ich in einem anderen Kommentar gepostet habe. Das geht kaum noch.
Ephemient
1
1-NRist länger als c++. Ja, ich weiß, dass es ein Charakter ist. Wie ich schon sagte - der wahre Grund ist, dass ich es vergessen habe. Wie auch immer, es sieht so aus, als würden die Leute die Schönheit von awk hier nicht schätzen.
Michael Krelin - Hacker
Und NRist kürzer als ++c:)
ephemient
3

Sie können die eigenen Such- und Ersatzfunktionen von Vim wie folgt verwenden: Setzen Sie den Cursor in die erste Zeile und geben Sie im normalen Modus Folgendes ein:

:.,/fff/s/\n.*\(\n.*\)/\1/g
  • Dies .,/fff/ist der Bereich für die Substitution. Es bedeutet "von dieser Zeile zu der Zeile, die dem regulären Ausdruck entspricht fff(in diesem Fall die letzte Zeile).
  • s///ist der Ersatzbefehl. Es sucht nach einem regulären Ausdruck und ersetzt jedes Vorkommen durch einen String. Das gam Ende bedeutet, die Ersetzung zu wiederholen, solange der reguläre Ausdruck gefunden wird.
  • Der \n.*\(\n.*\)reguläre Ausdruck entspricht einer neuen Zeile, dann einer ganzen Zeile ( .*entspricht einer beliebigen Anzahl von Zeichen mit Ausnahme einer neuen Zeile), dann einer weiteren neuen Zeile und einer weiteren Zeile. Die Klammern \(und \)bewirken, dass der Ausdruck in ihnen aufgezeichnet wird, damit wir ihn später verwenden können \1.
  • \1 Fügt die gruppierte neue Zeile und die Zeile danach wieder ein, da wir nicht möchten, dass auch die nächste Zeile weg ist. Wir möchten nur, dass der Substitutionsmechanismus daran vorbeigeht, damit wir sie beim nächsten Ersetzen nicht löschen.

Auf diese Weise können Sie den Bereich steuern, in dem das Löschen erfolgen soll, und Sie müssen kein externes Tool verwenden.

Daniel Hershcovich
quelle
Oder $stattdessen /fff/für das Dateiende, unabhängig vom Inhalt der Datei.
Ephemient
3

Als weiteren Ansatz können Sie auch Python verwenden, wenn Ihr vim dies unterstützt.

:py import vim; cb = vim.current.buffer; b = cb[:]; cb[:] = b[::2]

b = cb[:]kopiert vorübergehend alle Zeilen im aktuellen Puffer nach b. b[::2]holt jede zweite Zeile aus dem Puffer und weist sie dem gesamten aktuellen Puffer zu cb[:]. Das Kopieren nach bist erforderlich, da Pufferobjekte die erweiterte Slice-Syntax nicht zu unterstützen scheinen.

Dies ist wahrscheinlich nicht der "vim Weg", könnte aber leichter zu merken sein, wenn Sie Python kennen.

qzr
quelle
2

So löschen Sie ungerade Zeilen (1,3,5, ..) -> :%s/\(.*\)\n\(.*\)\n/\2\r/g

Gerade Zeilen löschen (2,4,6, ..) -> :%s/\(.*\)\n.*\n/\1\r/g

Suchen Sie nach Text (bildet die erste Zeile), gefolgt von einem neuen Zeilenzeichen und etwas mehr Text (bildet die zweite Zeile), gefolgt von einem weiteren neuen Zeilenzeichen, und ersetzen Sie das obige durch entweder erste Übereinstimmung (ungerade Zeile) oder zweite Übereinstimmung (gerade Zeile). gefolgt von Wagenrücklauf.

kartik kailas
quelle
0

Sed aufrufen:

:% !sed -e '2~2 d'

^^^^                  pipe file through a shell command
    ^^^^^^            the command is sed, and -e describes an expression as parameter
            ^^^^^     starting with the second line, delete every second line
asdmin
quelle
-1

Lösung

Sie können dies in vim versuchen

:1,$-1g/^/+1d

bearbeiten

Ich werde versuchen, klarer zu sein (zwei Teile)

erster Teil der Auswahlbereich ( :from,to)
zweiter Teil die Aktion ( dlöschen)

  • der Bereich von der ersten bis zur vorletzten Zeile (:1,$-1 )
  • globaly ( g) delete ( +1d) nächste Zeile

Sie können versuchen, in die erste Zeile Ihrer Datei zu wechseln und diese auszuführen. :+1dDie nächste Zeile wird ausgeblendet

Ich weiß, dass es schlammig ist, aber es ist vim und es funktioniert

wdog
quelle
Klar wie Schlamm. Vielen Dank!
Henry Henrinson