51 Stimmen

Wie kann ich in einem Verzeichnis in Vim rekursiv suchen und ersetzen?

Ich habe den Ersatzbefehl von Vim kennengelernt...

:%s/replaceme/replacement/gi

Und vimgrep...

:vimgrep  /findme/gj  project/**/*.rb

Gibt es eine Möglichkeit, sie zu kombinieren, um eine Ersetzung für alle Dateien in einem Verzeichnis vorzunehmen?

84voto

meleyal Punkte 30244

args y argdo sollte das tun, was Sie brauchen, z. B.

:args spec/javascripts/**/*.* 
:argdo %s/foo/bar/g

Siehe diese Seite .

78voto

Laurence Gonsalves Punkte 131009

Obwohl ich ein Vim-Benutzer bin, verwende ich im Allgemeinen find y sed für diese Art von Dingen. Die Regex-Syntax für sed ist dem von Vim ähnlich (beide sind Nachkommen von ed ), wenn auch nicht identisch. Mit GNU sed können Sie auch -i für In-Place-Bearbeitungen (normalerweise sed gibt die geänderte Version auf stdout aus).

Zum Beispiel:

find project -name '*.rb' -type f -exec sed -i -e 's/regex/replacement/g' -- {} +

Stück für Stück:

  • project = Suche im "Projekt"-Baum
  • -name '*.rb' = für Dinge, deren Namen mit '*.rb' übereinstimmen
  • -type f = und sind normale Dateien (keine Verzeichnisse, Pipes, Symlinks usw.)
  • -exec sed = laufen sed mit diesen Argumenten:
    • -i = mit In-Place-Bearbeitungen
    • -e 's/regex/replacement/g' = einen "substitute"-Befehl ausführen (der fast identisch ist mit Vims :s aber beachten Sie das Fehlen von % -- es ist die Standardeinstellung in sed )
    • -- = Ende der Flaggen, Dateinamen beginnen hier
    • {} = dies sagt find dass die gefundenen Dateinamen hier in die Befehlszeile von sed eingefügt werden sollen
    • + = dies sagt find dass die -exec Aktion abgeschlossen ist, und wir wollen die Argumente in möglichst wenigen sed Aufrufen wie möglich (im Allgemeinen effizienter als die einmalige Ausführung pro Dateiname). Um es einmal pro Dateiname auszuführen, können Sie verwenden \; anstelle von + .

Dies ist die allgemeine Lösung mit GNU sed y find . In besonderen Fällen kann dies ein wenig verkürzt werden. Wenn Sie zum Beispiel wissen, dass Ihr Namensmuster auf keine Verzeichnisse passt, können Sie -type f . Wenn Sie wissen, dass keine Ihrer Dateien mit einem - können Sie weglassen -- . Hier ist eine Antwort, die ich auf eine andere Frage gepostet habe, mit mehr Details zur Weitergabe von Dateinamen, die mit find zu anderen Befehlen.

9voto

proton1 Punkte 81

Um die ursprüngliche Frage nach der Kombination von vimgrep und substitute zu beantworten:

:vimgrep /pattern/ ./**/files              # results go to quickfix list
:set autowrite                             # auto-save when changing buffers
:silent! cdo %s/replaceme/replacement/gic  # cdo draws filenames from quickfix list
:set noautowrite

Lassen Sie die c de gic wenn Sie nicht jede Ersetzung bestätigen wollen (in diesem Fall könnten Sie auch find y sed wie von @LaurenceGonsalves vorgeschlagen).

8voto

Dave Kirby Punkte 24272

Sie könnten vimgrep ausführen und dann ein Makro aufzeichnen, das jede Zeile in der vimgrep-Ausgabe ansteuert und die Ersetzung vornimmt, etwa so: (geben Sie die #-Kommentare nicht ein!)

:set autowrite                  #automatically save the file when we change buffers
:vimgrep /pattern/ ./**/files  
qa                              #start recording macro in register a
:s/pattern/replace/g            #replace on the current line
:cnext                          #go to next matching line
q                               #stop recording
10000@a                          #repeat the macro 10000 times, or until stopped by 
                                #an "error" such as reaching the end of the list
:set noautowrite

Ich habe es nicht getestet, daher kann es ein wenig Optimierungsbedarf geben - testen Sie es zuerst mit einigen Dateien, die Sie nicht stören, wenn sie durcheinander geraten.

Der Vorteil gegenüber sed ist, dass Vim-Regex mächtiger sind und Sie die Option c zu :s hinzufügen können, um jede Ersetzung zu verifizieren.

Modifier : Ich habe meinen ursprünglichen Beitrag geändert, da er falsch war. Ich habe :1vimgrep verwendet, um die erste Übereinstimmung in jeder Datei zu finden, aber ich habe die Dokumentation falsch gelesen - es findet nur eine Übereinstimmung in allen Dateien.

6voto

hgmnz Punkte 13020

Eine Möglichkeit wäre, alle Dateien zu öffnen, für die Sie die Ersetzung durchführen möchten (oder jeden beliebigen Befehl), und Folgendes auszuführen :bufdo <cmd> , die dann <cmd> bei allen offenen Puffern.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X