Suchen und ersetzen Sie in Vim alle Projektdateien

117

Ich suche nach dem besten Weg, um (mit Bestätigung) alle Projektdateien in Vim zu suchen und zu ersetzen. Mit "Projektdateien" meine ich Dateien im aktuellen Verzeichnis, von denen einige nicht geöffnet sein müssen.

Eine Möglichkeit, dies zu tun, könnte darin bestehen, einfach alle Dateien im aktuellen Verzeichnis zu öffnen:

:args ./**

und dann suchen und ersetzen Sie alle geöffneten Dateien:

:argdo %s/Search/Replace/gce

Wenn ich dies tue, springt die Speichernutzung von Vim jedoch von ein paar Dutzend MB auf über 2 GB, was für mich nicht funktioniert.

Ich habe auch das EasyGrep- Plugin installiert, aber es funktioniert fast nie - entweder findet es nicht alle Vorkommen oder es hängt nur, bis ich drücke CtrlC. Bisher mein bevorzugter Weg , um diese Aufgabe zu erfüllen , es zu ack-grep für den Suchbegriff ein , es ist quickfix Fenster einer beliebige Datei öffnen verwenden, die den Begriff und wurde nicht vor dem Öffnen, und schließlich :bufdo %s/Search/Replace/gce.

Ich suche entweder nach einem guten, funktionierenden Plugin, das dafür verwendet werden kann, oder nach einem Befehl / einer Folge von Befehlen, die einfacher wären als der, den ich jetzt verwende.

Psyho
quelle
1
@Cascabel Seit Sie diesen Kommentar geschrieben haben, gibt es eine vi.stackexchange.com-Site.
Flimm

Antworten:

27

Greplace funktioniert gut für mich.

Es gibt auch eine Pathogen-fähige Version auf Github.

Smokingfliege
quelle
8
Installiert sehe ich die anderen Befehle wie :Gsearchund :Gbuffersearchexistiere, aber wenn ich tippe, :Greplacebekomme ich Not an editor command: Greplace.
Ramon Tayag
Laut den Dokumenten lautet die Syntax: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] So können Sie eine rekursive Suche durchführen, indem Sie beispielsweise :Gsearch -r pattern dir/
Folgendes verwenden
Das, was ich an Greplace hasse, ist, dass Greplace fehlschlägt, wenn sich das Muster im Dateinamen befindet, weil Sie es letztendlich auch im Dateinamen ersetzen.
Daniel
2
Nach 6 Jahren hier nichts Ungewöhnliches, aber dieses Plugin ist im Vergleich zu einigen neuen Antworten ( github.com/yegappan/greplace ) vor mehr als 3 Jahren stark veraltet . Ich könnte die eingebaute Lösung von Sid überprüfen: stackoverflow.com/a/38004355/2224331 (Ich bin es nicht aus einem anderen Grund, nur ein Zufall: P)
SidOfc
99

Die andere große Option hier ist einfach, vim nicht zu verwenden:

sed -i 's/pattern/replacement/' <files>

oder wenn Sie eine Möglichkeit haben, eine Liste von Dateien zu erstellen, vielleicht so etwas:

find . -name *.cpp | xargs sed -i 's/pattern/replacement/'
grep -rl 'pattern1' | xargs sed -i 's/pattern2/replacement/'

und so weiter!

Cascabel
quelle
Vielen Dank! Ich muss wirklich dieses Kommandozeilen-Zeug der alten Schule lernen. Ich denke, das ist die beste Antwort. Sind diese Regexe Perl-Regexe oder Vim-Regexe oder eine andere Art von Regex?
Eric Johnson
2
@EricJohnson: Es handelt sich um ... sedreguläre Ausdrücke, bei denen es sich im Wesentlichen um reguläre POSIX.2-Grundausdrücke handelt, aber nicht genau (dies steht auf der Manpage) - sie lassen \nZeilenumbrüche und einige ähnliche Dinge übereinstimmen. Sie sind daher so ziemlich die gleichen wie grepreguläre Ausdrücke.
Cascabel
Gibt es einen Grund, warum Sie -i im Befehl sed haben? Auf der Manpage wird angegeben, dass eine Erweiterung angegeben werden soll, aber Sie scheinen keine tatsächlich anzugeben.
Davekaro
Ok, es sieht tatsächlich so aus, als wäre 's / pattern / replace /' die Erweiterung, ich glaube ich verstehe jetzt.
Davekaro
11
Unter Mac OS X war der einzige sed-Befehl, der für mich funktionierte:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss
74

BEARBEITEN: Verwenden Sie den cfdoBefehl anstatt cdo, um die Anzahl der Befehle, die ausgeführt werden, um dies zu erreichen, erheblich zu reduzieren (da cdoBefehle für jedes Element ausgeführt werden, während cfdoBefehle für jede Datei ausgeführt werden).

Dank des kürzlich hinzugefügten cdoBefehls können Sie dies jetzt in zwei einfachen Befehlen mit dem von grepIhnen installierten Tool ausführen . Keine zusätzlichen Plugins erforderlich!:

1. :grep <search term>
2. :cdo %s/<search term>/<replace term>/gc
3. (If you want to save the changes in all files) :cdo update

( cdoFührt den gegebenen Befehl zu jedem Begriff in der Quickfix ist , der grepBefehl bevölkert.)

(Entfernen Sie das cam Ende des 2. Befehls, wenn Sie jeden Suchbegriff ersetzen möchten, ohne dies jedes Mal zu bestätigen.)

Sid
quelle
12
Außerdem können Sie hinzufügen :cdo update, um die Änderungen in allen Dateien zu speichern.
Zhe Li
1
Es wird jedoch angehalten, um nicht jedes Mal zu warnen, bevor zum nächsten Puffer gewechselt wird, wenn der aktuelle Puffer geändert wird.
Kostenlos
1
@Zhe danke, das habe ich der Antwort hinzugefügt; @lfree Ich persönlich bevorzuge es, jede Änderung zu bestätigen, aber Sie können den cam Ende des zweiten Befehls entfernen (einfach verwenden g), um ihn suchen und ersetzen zu lassen, ohne um Bestätigung zu bitten
Sid
1
: cdo% s / Suchbegriff / Begriff ersetzen / g ersetzen nur das erste Vorkommen, funktioniert aber nicht für das zweite Vorkommen in meinem vim
sunxd
3
für die Verwendung updatemusste ich:cdo %s/<search term>/<replace term>/g | update
gabe
34

Ich habe mich entschieden, ack und Perl zu verwenden, um dieses Problem zu lösen, um die leistungsstärkeren regulären Perl-Vollausdrücke anstelle der GNU-Teilmenge zu nutzen.

ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'

Erläuterung

ack

ack ist ein wunderbares Kommandozeilen - Tool , das eine Mischung aus ist grep, findund volle Perl reguläre Ausdrücke (nicht nur die GNU Teilmenge). Es ist in reinem Perl geschrieben, schnell, hat Syntaxhervorhebung, funktioniert unter Windows und ist für Programmierer freundlicher als die herkömmlichen Befehlszeilentools. Installiere es auf Ubuntu mit sudo apt-get install ack-grep.

xargs

Xargs ist ein altes Unix-Befehlszeilentool. Es liest Elemente aus der Standardeingabe und führt den angegebenen Befehl aus, gefolgt von den für die Standardeingabe gelesenen Elementen. Grundsätzlich wird die Liste der von ack generierten Dateien an das Ende des perl -pi -E 's/pattern/replacemnt/g'Befehls angehängt .

Perl-pi

Perl ist eine Programmiersprache. Die Option -p bewirkt, dass Perl eine Schleife um Ihr Programm erstellt, die über Dateinamenargumente iteriert. Die Option -i bewirkt, dass Perl die Datei an Ort und Stelle bearbeitet. Sie können dies ändern, um Sicherungen zu erstellen. Die Option -E bewirkt, dass Perl die eine als Programm angegebene Codezeile ausführt. In unserem Fall ist das Programm nur eine Perl-Regex-Substitution. Weitere Informationen zu Perl-Befehlszeilenoptionen perldoc perlrun. Weitere Informationen zu Perl finden Sie unter http://www.perl.org/ .

Eric Johnson
quelle
Ich mag diese Antwort, weil sie sogar mit Cygwins Zeilenenden funktioniert. Beachten Sie, dass am Ende geänderter Zeilen ein \ r hinzugefügt wird. Bei Verwendung von sed wird jedoch jede neue Zeile ersetzt, wodurch Ihre Unterschiede unbrauchbar werden. Perl ist ein bisschen vernünftiger, und dies ist ein wirklich großartiger Einzeiler zum Suchen und Ersetzen. Vielen Dank!
Andrew
@ Andrew, ich bin nicht sicher, was genau in Ihrem Fall falsch läuft. Würde es Ihnen helfen, \ R anstelle von \ n in Ihren regulären Ausdrücken zu verwenden? Siehe Abschnitt \ R in perldoc.perl.org/perlrebackslash.html#Misc . Versuchen Sie auch perldoc.perl.org/perlre.html#Regular-Expressions . Perl ist extrem plattformübergreifend und ich bin sicher, es gibt eine Möglichkeit für Sie, nicht mit verstümmelten Zeilenenden zu enden.
Eric Johnson
Mein Regex enthält keine Zeilenumbrüche. Die Cygwin-Versionen dieser Programme fügen automatisch \ r am Ende jeder geänderten Zeile hinzu. Ich mochte die Perl-Version besonders, weil sie nur die Zeilen ändert, mit denen sie übereinstimmt, während sed alle Zeilen ändert , auch wenn sie nicht mit dem regulären Ausdruck übereinstimmen.
Andrew
Was ist, wenn der Pfad der Datei Leerzeichen enthält?
Shengy
23

vielleicht mach das:

:noautocmd vim /Search/ **/*
:set hidden
:cfirst
qa
:%s//Replace/gce
:cnf
q
1000@a
:wa

Erläuterung:

  • :noautocmd vim /Search/ **/*⇒ Suchmuster ( vimist eine Abkürzung für vimgrep) in allen Dateien in allen Unterverzeichnissen des cwd, ohne autocmds ( :noautocmd) aus Gründen der Geschwindigkeit auszulösen .
  • :set hidden ⇒ Zulassen, dass geänderte Puffer nicht in einem Fenster angezeigt werden (möglicherweise in Ihrem vimrc)
  • :cfirst ⇒ zum ersten Suchergebnis springen
  • qa ⇒ Starten Sie die Aufnahme eines Makros in Register a
  • :%s//Replace/gce⇒ Ersetzen Sie alle Vorkommen des letzten Suchmusters (noch /Search/zu diesem Zeitpunkt) durch Replace:
    • mehrmals in derselben Zeile ( gFlagge)
    • mit Benutzerbestätigung ( cFlag)
    • ohne Fehler, wenn kein Muster gefunden wurde ( eFlag)
  • :cnf⇒ Zur nächsten Datei in der vom vimBefehl erstellten Liste springen
  • q ⇒ Aufnahme des Makros beenden
  • 1000@a ⇒ Spielmakro 1000-mal im Register gespeichert
  • :wa ⇒ Speichern Sie alle geänderten Puffer

* EDIT * Vim 8 Weg:

Ab Vim 8 gibt es eine bessere Möglichkeit, da :cfdoalle Dateien in der Quickfix-Liste wiederholt werden:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa
Benoit
quelle
Vielleicht möchten Sie das für uns kleinere Sterbliche etwas näher erläutern. Mir ist nicht klar, ob Sie diese "Sequenz" jedes Mal machen müssen. Ich mache das schneller mit meinem rostigen alten Delphi 5, also muss mir sicher etwas fehlen.
Lieven Keersmaekers
1
@Lieven: Du hast recht, das ist ein bisschen dunkel ohne Kommentare. Ich werde eine Erklärung entwickeln.
Benoit
+1, aber obwohl vimes mich für viele Dinge überzeugt hat, denke ich, dass ich dabei bei PowerGrep bleiben werde.
Lieven Keersmaekers
Vielen Dank für Ihre Antwort und die Erklärung aller Befehle. Ich weiß das sehr zu schätzen. Ich akzeptiere die andere Antwort, weil ich dafür lieber ein Plugin verwende.
Psyho
Ich habe dies notiert, weil es mehr Verwirrung stiftet, da die Ebene zwischen jemandem, der die Frage stellt, und der Ebene, die zum Verständnis dieser Antwort erforderlich ist, unterschiedlich ist.
Lloyd Moore
22

Füllen Sie :argsaus einem Shell-Befehl

Es ist möglich (unter einigen Betriebssystemen 1 ), die Dateien :argsüber einen Shell-Befehl bereitzustellen .

Wenn Sie beispielsweise ack 2 installiert haben,

:args `ack -l pattern`

wird ack bitten, eine Liste von Dateien zurückzugeben, die 'pattern' enthalten, und diese in die Argumentliste aufzunehmen.

Oder mit einfachem altem Grep, denke ich, wäre es:

:args `grep -lr pattern .`  


Sie können dann einfach :argdowie vom OP beschrieben verwenden:

:argdo %s/pattern/replacement/gce


Füllen Sie :argsaus der Quickfix-Liste

Lesen Sie auch die Antwort von nelstrom auf eine verwandte Frage, die einen einfachen benutzerdefinierten Befehl beschreibt, der die Arglist aus der aktuellen Quickfix-Liste auffüllt. Dies funktioniert hervorragend mit vielen Befehlen und Plugins, deren Ausgabe in der Quickfix-Liste ( :vimgrep, :Ack3 , :Ggrep4 ) landet .

Die Sequenz zum Durchführen einer projektweiten Suche könnte dann folgendermaßen ausgeführt werden:

:vimgrep /pattern/ **/*
:Qargs 
:argdo %s/findme/replacement/gc

Wo :Qargsist der Aufruf des benutzerdefinierten Befehls, der die Arglist aus der Quickfix-Liste auffüllt?

In der folgenden Diskussion finden Sie auch Links zu einfachen Plugins, die diesen Workflow auf 2 oder 3 Befehle reduzieren.

Links

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. Flüchtling - github.com/tpope/vim-fugitive
exbinary
quelle
1
Argdo stoppt nach der ersten Datei mit einem Schreibproblem
Frankster
9

Eine weitere Option im Jahr 2016, far.vim Plugin:

far.vom

Oleg Khalidov
quelle
4
Sie sollten auch sagen, dass Sie der Autor des Plugins sind :)
Sitilge
5

Wenn es Ihnen nichts ausmacht, externe Abhängigkeiten einzuführen, habe ich ein Plugin ctrlsf.vim (abhängig von ack oder ag ) erstellt, um die Aufgabe zu erledigen.

Es kann das Suchergebnis von ack / ag formatieren und anzeigen und Ihre Änderungen im Ergebnispuffer mit den tatsächlichen Dateien auf der Festplatte synchronisieren.

Vielleicht erklärt die folgende Demo mehr

ctrlsf_edit_demo

dyng
quelle
3
1. :grep <search term> (or whatever you use to populate the quickfix window)
2. :cfdo %s/<search term>/<replace term>/g | update

Schritt 1 füllt die Quickfix-Liste mit den gewünschten Elementen. In diesem Fall wird es mit Suchbegriffen weitergegeben, über die Sie Änderungen vornehmen möchten grep.

cfdoführt den folgenden Befehl für jede Datei in der Quickfix-Liste aus. Geben Sie :help cfdofür Details ein.

s/<search term>/<replace term>/gersetzt jeden Begriff. /gbedeutet, jedes Vorkommen in der Datei zu ersetzen.

| update speichert die Datei nach jedem Austausch.

Ich habe dies basierend auf dieser Antwort und ihren Kommentaren zusammengesetzt, aber ich hatte das Gefühl, dass es eine eigene Antwort verdient, da alles an einem Ort ist.

Kyle Heironimus
quelle
Bei NeoVim musste in Zeile 2 oben eine geringfügige Änderung vorgenommen werden: 2. :cfdo %s/<search term>/<replace term>/g | updateOhne das %wird nur das erste Auftreten des Musters ersetzt.
Kareem Ergawy
Danke Kareem. Ich habe die Antwort aktualisiert, da sie auch %smit normalem vim funktioniert.
Kyle Heironimus
0

Grundsätzlich wollte ich das Ersetzen in einem einzigen Befehl und vor allem in vim selbst

Basierend auf der Antwort von @Jefromi habe ich eine Tastenkombination erstellt, die ich in meiner .vimrc-Datei wie folgt festgelegt hatte

nmap <leader>r :!grep -r -l  * \| xargs sed -i -e 's///g'

jetzt von der vim, auf einen Strich von führer + r bekomme ich den Befehl in vim geladen, den ich wie unten bearbeite,

:!grep -r -l <find> <file pattern> | xargs sed -i -e 's/<find>/<replace>/g'

Daher ersetze ich mit einem einzigen Befehl und vor allem innerhalb von vim

deepak
quelle
und ich benutze tatsächlich ag anstelle von grep
deepak