2010-01-21 3 views
32

Ich fand heraus, über Vims Ersatz Befehl ...Wie kann ich rekursiv in einem Verzeichnis in Vim suchen und ersetzen?

:%s/replaceme/replacement/gi 

Und vimgrep ...

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

Gibt es eine Möglichkeit, sie zu kombinieren, um einen Ersatz für alle Dateien unter einem zu tun Verzeichnis?

+0

[Hier] (http: // stackoverflow.com/a/13944042/834176) ist eine Möglichkeit, rekursiv zu suchen und zu ersetzen, aber nur bei Dateien unter Versionskontrolle (es könnte einfach verallgemeinert werden, wenn Sie wirklich * alle * Dateien in einem Verzeichnis durchsuchen wollen). –

Antwort

47

Obwohl ich ein Vim-Benutzer bin, verwende ich find und sed für diese Art von Sache. Die Regex-Syntax für sed ist ähnlich zu Vim (sie sind beide Nachkommen von ed), obwohl nicht identisch. Mit GNU sed können Sie auch -i für In-Place-Edits verwenden (normalerweise gibt sed die modifizierte Version an stdout aus).

Zum Beispiel:

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

Stück für Stück:

  • proj = Suche in "proj" Baum
  • -name '*.rb' = für Dinge, deren Namen '* .rb'
  • -type f = und sind reguläre Dateien (keine Verzeichnisse, Pipes, Symlinks usw.))
  • -exec sed = sed mit diesen Argumenten ausführen:
    • -i = mit an Ort und Stelle bearbeitet
    • -e 's/regex/replacement/g' = einen "Ersatz" Befehl (die fast identisch mit Vims :s, führen aber beachte Mangel an % - es ist der Standard in sed)
    • -- = Ende der Fahnen, Dateinamen beginnen hier
    • {} = das sagt find, die th e Dateinamen gefunden, sollte hier das Befehlszeilen sed platziert werden
    • + = this find sagt, dass die -exec Aktion beendet ist, und wir wollen die Argumente in möglichst wenigen sed Anrufungen wie möglich Gruppe (in der Regel effizienter, als es einmal läuft pro Dateiname). Um es einmal pro Dateiname auszuführen, können Sie \; anstelle von + verwenden.

Dies ist die allgemeine Lösung mit GNU sed und find. Dies kann in besonderen Fällen etwas verkürzt werden. Wenn Sie beispielsweise wissen, dass Ihr Namensmuster keinen Verzeichnissen entspricht, können Sie -type f weglassen. Wenn Sie wissen, dass keine Ihrer Dateien mit einer - beginnt, können Sie -- weglassen. Here's an answer I posted to another question with more details on passing filenames found with find to other commands.

+6

Stellen Sie sicher, und testen Sie Ihre 'find', bevor Sie es zu' sed' geben - das kann sehr schlecht sein, wenn es übermäßig inklusive ist. Es ist auch eine gute Idee, 'sed' die Option' -c' ('--copy') zu geben, die verhindert, dass sie mit schreibgeschützten Dateien in Konflikt kommt. – Cascabel

+0

@BroSlow Fair genug. Ich habe die Antwort aktualisiert, um sicherer/allgemeiner zu sein. (Unter Linux ist zumindest ARG_MAX ziemlich groß, und für den in der Frage beschriebenen Anwendungsfall wären Dateinamen mit Leerzeichen oder Sonderzeichen ziemlich seltsam.) –

+0

Wenn Sie diese Methode verwenden, können Sie die Konformation nicht verwenden, weil sed unterstützt es nicht, Sie können 'vim -c '%/regex/replacement/gc' -c 'wq' - {} \;' verwenden, damit Sie Ihre Änderungen auch bestätigen können – Funonly

4

Es gibt ein Plugin, das genau das zu tun versucht: Vimgrep Replace.

Oh, warte ... gibt es andere: Global Replace, EasyGrep.

Für eine nicht-Plugin Lösung, vielleicht argdo würde helfen, wenn Sie vimgrep ‚s Ausgang auf die Argumentliste bekommen konnte (die mit args eingestellt werden kann), aber ich kann nicht die Details scheinen, um herauszufinden. Ich würde mich freuen, wenn jemand die Idee übernehmen und verbessern würde ... also hier ist es.

Die Grundidee hinter der ersten Plugin (ich auch die andere erraten würde ...) ist vimgrep zuerst, dann Zyklus durch die Spiele mit :cnext und Anwendung der Substitution Befehl in jeder Zeile zu verwenden. Eine Funktion, um dies zu erreichen, könnte klein genug sein, um es in .vimrc zu setzen. (Vielleicht können Sie einen aus den Quellen der Plugins heben?)

(Ich nehme an, hgimenez ist vielleicht auf eine Lösung, aber ob es angebracht ist oder nicht, würde wahrscheinlich von der Anzahl der zu verarbeitenden Dateien abhängen ... Die Plugins sollte in Ordnung sein unabhängig.)

+0

Danke, aber Arrrrgh! Gibt es eine Möglichkeit dies zu tun, ohne ein weiteres #! * &% Plugin zu installieren? – Ethan

+0

@Ethan: Ich fühle für dich. :-) Ich denke es muss einen Weg geben mit: args,: argdo,: copen oder was auch immer ... Ich benutze einfach nicht genug, um vorher viel darüber nachgedacht zu haben und kann nicht in der Lage sein sich zusammen zu finden die perfekte Lösung für dich gerade noch. Aber ich versuche es immer noch, also werde ich mir etwas einfallen lassen und es bearbeiten. –

+0

Naja ... Ich kann wirklich keine saubere und einfache No-Plugin-Lösung finden oder schlüssig versichern, dass es keine gibt. Mein Vim-fu versagt mich! :-(Die Plugins scheinen nett zu sein, aber vielleicht wäre es einen Versuch wert ... –

4

Eine Möglichkeit wäre, alle öffnen Sie die Dateien, die Substitution an (oder einen Befehl) ausgeführt werden soll, und führen Sie :bufdo <cmd>, die <cmd> an allen offenen Puffer laufen wird.

7

Sie führen könnte vimgrep dann ein Makro aufzeichnen, die in der vimgrep Ausgang jeder Zeile geht und nicht das ersetzen, so etwas wie folgt aus: (! Art in den # Kommentare nicht)

: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 
[email protected]       #repeat the macro 10000 times, or until stopped by 
           #an "error" such as reaching the end of the list 
:set noautowrite 

habe ich nicht getestet es, so dass es ein wenig zwicken müssen - testen Sie es auf einige Dateien, die Sie nichts dagegen haben, zuerst versaut.

Der Vorteil dieser über sed ist, dass Vim Regex leistungsfähiger sind, und Sie können die Option c hinzufügen, um: s, um jeden Ersatz zu überprüfen.

Bearbeiten: Ich habe meinen ursprünglichen Beitrag geändert, da es falsch war. Ich verwendete: 1vimgrep, um die erste Übereinstimmung in jeder Datei zu finden, aber ich habe die Dokumente falsch gelesen - es findet nur eine Übereinstimmung über alle Dateien.

+0

Cool! Auf der Suche nach einer einzigen Ausdruckslösung habe ich nie aufgehört, ein Makro zu betrachten ... Macht mich jetzt irgendwie albern. +1. :-) –

+0

Du hast mir gerade Stunden Arbeit gerettet, danke !!! : D Ich musste einen Wert in einer XML-Datei in einen anderen Teil der Datei kopieren und dann die Groß- und Kleinschreibung übernehmen - über 200 Dateien. – Jason

45

args und argdo sollten tun, was Sie brauchen, z.B.

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

Siehe this page.

+0

Danke! Es ist sehr cool :) – starikovs

+1

Whoa vimskill ++ –

+1

Jahren des Leidens und der Befragung der Berufswahl endete heute –

0

Dies funktioniert für eine kleine Anzahl von Dateien.

vim `finden. -type f `

: bufdo% s/muster/ersatz/gec | update

Verwandte Themen