Cerca e sostituisci in Vim in tutti i file di progetto


117

Sto cercando il modo migliore per eseguire la ricerca e la sostituzione (con conferma) in tutti i file di progetto in Vim. Per "file di progetto" intendo i file nella directory corrente, alcuni dei quali non devono essere aperti.

Un modo per farlo potrebbe essere semplicemente aprire tutti i file nella directory corrente:

:args ./**

e poi fai la ricerca e sostituisci su tutti i file aperti:

:argdo %s/Search/Replace/gce

Tuttavia, quando lo faccio, l'utilizzo della memoria di Vim passa da un paio di dozzine di MB a oltre 2 GB, il che non funziona per me.

Ho anche installato il plugin EasyGrep , ma non funziona quasi mai: o non trova tutte le occorrenze o si blocca finché non premo CtrlC. Finora il mio modo preferito per eseguire questa operazione è ack-grep per il termine di ricerca, utilizzando la sua finestra di correzione rapida apri qualsiasi file che contiene il termine e non è stato aperto prima, e infine :bufdo %s/Search/Replace/gce.

Sto cercando un buon plugin funzionante che possa essere utilizzato per questo, o in alternativa un comando / sequenza di comandi che sarebbe più semplice di quello che sto usando ora.


1
@Cascabel Da quando hai scritto questo commento, c'è un sito vi.stackexchange.com.
Flimm

Risposte:


27

Greplace funziona bene per me.

C'è anche una versione pronta per i patogeni su GitHub.


8
Installato, vedo gli altri comandi simili :Gsearched :Gbuffersearchesistono, ma quando digito :Greplaceottengo Not an editor command: Greplace.
Ramon Tayag

secondo i documenti, la sintassi è: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] quindi potresti fare una ricerca ricorsiva usando, ad esempio::Gsearch -r pattern dir/
vitorbal

La cosa che odio di Greplace è che se il pattern è nel nome del file, Greplace fallisce, perché finisci per sostituirlo anche nel nome del file.
Daniel

2
Niente di insolito qui dopo 6 anni, ma questo plugin è decisamente obsoleto rispetto ad alcune nuove risposte ( github.com/yegappan/greplace ) dell'ultimo aggiornamento, 3+ anni fa. Potrei controllare la soluzione integrata di Sid: stackoverflow.com/a/38004355/2224331 (Non sono io su un altro account, solo una coincidenza: P)
SidOfc

99

L'altra grande opzione qui è semplicemente non usare vim:

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

o se hai un modo per generare un elenco di file, forse qualcosa del genere:

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

e così via!


Grazie! Ho davvero bisogno di imparare questa roba da riga di comando della vecchia scuola. Penso che questa sia la risposta migliore. Queste regex sono regex perl o regex vim o qualche altro tipo di regex?
Eric Johnson,

2
@EricJohnson: Sono ... sedregex, che sono essenzialmente espressioni regolari di base POSIX.2, ma non esattamente (questo è nella manpage) - lasciano \ncorrispondere le nuove righe e alcune altre cose simili. Sono quindi più o meno le stesse grepdelle espressioni regolari.
Cascabel

C'è una ragione per cui hai -i nel comando sed? La pagina man dice che serve per specificare un'estensione, ma non sembra che tu ne specifichi effettivamente una.
davekaro

Ok, in realtà sembra che "s / pattern / replacement /" sia l'estensione, penso di aver capito ora.
davekaro

11
Su Mac OS X l'unico comando sed che ha funzionato per me era:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss

74

MODIFICA: usa il cfdocomando invece di cdoridurre in modo significativo la quantità di comandi che verranno eseguiti per ottenere ciò (perché cdoesegue i comandi su ogni elemento mentre cfdoesegue i comandi su ogni file)

Grazie al cdocomando aggiunto di recente , ora puoi farlo in due semplici comandi usando qualunque grepstrumento tu abbia installato. Nessun plugin aggiuntivo richiesto !:

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

( cdoesegue il comando dato a ciascun termine nell'elenco quickfix, che il tuo grepcomando popola.)

(Rimuovere calla fine del 2 ° comando se si desidera sostituire ogni termine di ricerca senza confermare ogni volta)


12
Inoltre puoi aggiungere :cdo updateper salvare le modifiche in tutti i file.
Zhe Li

1
Ma farà una pausa per avvertire che non è stato salvato ogni volta prima di passare al buffer successivo quando il buffer corrente viene modificato.
Libero

1
@Zhe grazie, l'ho aggiunto alla risposta; @lfree Personalmente preferisco confermare ogni modifica, ma puoi rimuovere calla fine del secondo comando (basta usare g) per farlo cercare e sostituire senza chiedere conferma
Sid

1
: cdo% s / termine di ricerca / sostituisci termine / g sostituisce solo la prima occorrenza, ma non funziona per la seconda occorrenza nel mio vim
sunxd

3
per l'utilizzo update, ho dovuto:cdo %s/<search term>/<replace term>/g | update
gabe

34

Ho deciso di utilizzare ack e Perl per risolvere questo problema in modo da sfruttare le più potenti espressioni regolari Perl complete piuttosto che il sottoinsieme GNU.

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

Spiegazione

ack

ACK è un fantastico strumento di riga di comando che è un mix di grep, finde completa le espressioni regolari Perl (non solo il sottoinsieme GNU). È scritto in puro Perl, è veloce, ha l'evidenziazione della sintassi, funziona su Windows ed è più amichevole per i programmatori rispetto ai tradizionali strumenti da riga di comando. Installalo su Ubuntu con sudo apt-get install ack-grep.

xargs

Xargs è un vecchio strumento da riga di comando unix. Legge gli elementi dallo standard input ed esegue il comando specificato seguito dagli elementi letti per lo standard input. Quindi, in pratica, l'elenco dei file generati da ack viene aggiunto alla fine del perl -pi -E 's/pattern/replacemnt/g'comando.

perl -pi

Perl è un linguaggio di programmazione. L'opzione -p fa sì che Perl crei un ciclo attorno al programma che itera sugli argomenti del nome del file. L'opzione -i fa in modo che Perl modifichi il file sul posto. Puoi modificarlo per creare backup. L'opzione -E fa in modo che Perl esegua l'unica riga di codice specificata come programma. Nel nostro caso il programma è solo una sostituzione di regex Perl. Per ulteriori informazioni sulle opzioni della riga di comando Perl perldoc perlrun. Per ulteriori informazioni su Perl, vedere http://www.perl.org/ .


Mi piace questa risposta perché funziona anche con le terminazioni di riga di Cygwin. Nota che aggiunge un \ r alla fine delle righe modificate, ma quando usi sed, sostituirà ogni nuova riga, rendendo inutilizzabili le differenze. Perl è un po 'più sano, e questo è davvero un ottimo one-liner per la ricerca e la sostituzione. Grazie!
andrew

@andrew, non sono sicuro di cosa stia andando storto nel tuo caso. Ti aiuterebbe a usare \ R invece di \ n nelle tue espressioni regolari? Vedere la sezione \ R in perldoc.perl.org/perlrebackslash.html#Misc . Prova anche perldoc.perl.org/perlre.html#Regular-Expressions . Perl è estremamente multipiattaforma e sono sicuro che c'è un modo per non finire con terminazioni di linea alterate.
Eric Johnson

La mia regex non contiene nuove righe, le versioni cygwin di questi programmi aggiungono automaticamente \ r alla fine di ogni riga modificata. In particolare mi è piaciuta la versione perl perché modifica solo le righe a cui corrisponde, mentre sed modificherà tutte le righe, anche se non corrispondono alla regex.
andrew

E se il percorso del file contiene spazi?
shengy

23

forse fai questo:

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

Spiegazione:

  • :noautocmd vim /Search/ **/*⇒ cercare ( vimè l'abbreviazione di vimgrep) pattern in tutti i file in tutte le sottodirectory del cwd senza attivare autocmds ( :noautocmd), per motivi di velocità.
  • :set hidden ⇒ consenti di avere buffer modificati non visualizzati in una finestra (potrebbe essere nel tuo vimrc)
  • :cfirst ⇒ vai al primo risultato della ricerca
  • qa ⇒ avviare la registrazione di una macro nel registro a
  • :%s//Replace/gce⇒ sostituire tutte le occorrenze dell'ultimo modello di ricerca (ancora /Search/in quel momento) con Replace:
    • più volte sulla stessa riga ( gbandiera)
    • con conferma utente ( cflag)
    • senza errore se non è stato trovato alcun pattern ( eflag)
  • :cnf⇒ passare al file successivo nell'elenco creato dal vimcomando
  • q ⇒ interrompere la registrazione della macro
  • 1000@a ⇒ riprodurre la macro memorizzata nel registro per 1000 volte
  • :wa ⇒ salva tutti i buffer modificati

* MODIFICA * Vim 8 vie:

A partire da Vim 8 c'è un modo migliore per farlo, poiché :cfdoitera su tutti i file nell'elenco quickfix:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa

potresti spiegarlo un po 'di più per noi mortali minori. Non mi è chiaro se devi fare questa "sequenza" ogni volta. Sono più veloce a farlo usando il mio vecchio Delphi 5 arrugginito, quindi sicuramente devo perdere qualcosa.
Lieven Keersmaekers

1
@Lieven: hai ragione, questo è un po 'oscuro senza commenti. Svilupperò alcune spiegazioni.
Benoit

+1 ma anche se vimmi ha conquistato per molte cose, penso che rimarrò fedele a PowerGrep con questo.
Lieven Keersmaekers

Grazie per la tua risposta e la spiegazione di tutti i comandi, lo apprezzo molto. Accetto l'altra risposta perché preferisco usare un plugin per questo.
psyho

L'ho segnato perché causa più confusione, data la disparità tra il livello di qualcuno che pone la domanda e il livello richiesto per comprendere questa risposta.
Lloyd Moore

22

Popola :argsda un comando di shell

È possibile (su alcuni sistemi operativi 1 )) fornire i file :argstramite un comando di shell.

Ad esempio, se hai installato ack 2 ,

:args `ack -l pattern`

chiederà a ack di restituire un elenco di file contenenti "pattern" e di inserirli nell'elenco degli argomenti.

O con il semplice grep, immagino che sarebbe:

:args `grep -lr pattern .`  


Puoi quindi usare semplicemente :argdocome descritto dall'OP:

:argdo %s/pattern/replacement/gce


Popolare :argsdall'elenco di correzione rapida

Controlla anche la risposta di nelstrom a una domanda correlata che descrive un semplice comando definito dall'utente che popola l'arglist dall'elenco corrente di quickfix. Funziona alla grande con molti comandi e plugin il cui output finisce nella lista di quickfix ( :vimgrep, :Ack3 , :Ggrep4 ).

La sequenza per eseguire una ricerca a livello di progetto potrebbe quindi essere eseguita con:

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

dove :Qargsè la chiamata al comando definito dall'utente che popola l'arglist dall'elenco quickfix.

Nella discussione successiva troverai anche collegamenti a semplici plug-in che riducono questo flusso di lavoro a 2 o 3 comandi.

link

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. fuggitivo - github.com/tpope/vim-fugitive

1
argdo si ferma dopo il primo file con un problema di scrittura
Frankster


5

Se non ti dispiace introdurre la dipendenza esterna, ho preparato un plugin ctrlsf.vim (dipende da ack o ag ) per fare il lavoro.

Può formattare e visualizzare i risultati della ricerca da ack / ag e sincronizzare le modifiche nel buffer dei risultati con i file effettivi sul disco.

Forse la seguente demo spiega di più

ctrlsf_edit_demo


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

Il passaggio 1 popola l'elenco di correzione rapida con gli elementi desiderati. In questo caso, viene propagato con i termini di ricerca che desideri modificare tramite grep.

cfdoesegue il comando seguente su ogni file nell'elenco quickfix. Digita :help cfdoper i dettagli.

s/<search term>/<replace term>/gsostituisce ogni termine. /gsignifica sostituire ogni occorrenza nel file.

| update salva il file dopo ogni sostituzione.

L'ho messo insieme sulla base di questa risposta e dei suoi commenti, ma ho ritenuto che meritasse una sua risposta poiché è tutto in un unico posto.


Per me su NeoVim, è stata necessaria una leggera modifica nella riga 2 sopra: 2. :cfdo %s/<search term>/<replace term>/g | updatesenza %, viene sostituita solo la prima occorrenza del pattern.
Kareem Ergawy

Grazie Kareem. Ho aggiornato la risposta poiché %sfunziona anche su vim regolare.
Kyle Heironimus

0

Fondamentalmente, volevo la sostituzione in un unico comando e, cosa più importante, all'interno di vim stesso

Sulla base della risposta di @Jefromi ho creato una scorciatoia da tastiera, che avevo impostato nel mio file .vimrc come questo

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

ora da vim, con un colpo di leader + r ottengo il comando caricato in vim, che modifico come sotto,

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

Quindi, eseguo la sostituzione con un singolo comando e, cosa più importante, all'interno di vim stesso


e io effettivamente uso ag invece di grep
deepak
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.