Busque y reemplace en Vim en todos los archivos del proyecto

117

Estoy buscando la mejor manera de buscar y reemplazar (con confirmación) en todos los archivos del proyecto en Vim. Por "archivos de proyecto" me refiero a archivos en el directorio actual, algunos de los cuales no tienen que estar abiertos.

Una forma de hacer esto podría ser simplemente abrir todos los archivos en el directorio actual:

:args ./**

y luego haga la búsqueda y reemplace en todos los archivos abiertos:

:argdo %s/Search/Replace/gce

Sin embargo, cuando hago esto, el uso de memoria de Vim salta de un par de docenas de MB a más de 2 GB, lo que no me funciona.

También tengo instalado el complemento EasyGrep , pero casi nunca funciona, o no encuentra todas las ocurrencias o simplemente se cuelga hasta que presiono CtrlC. Hasta ahora, mi forma preferida de realizar esta tarea es ack-grep para el término de búsqueda, utilizando su ventana de corrección rápida para abrir cualquier archivo que contenga el término y que no se haya abierto antes, y finalmente :bufdo %s/Search/Replace/gce.

Estoy buscando un complemento bueno y funcional que pueda usarse para esto, o alternativamente un comando / secuencia de comandos que sea más fácil que el que estoy usando ahora.

psyho
fuente
1
@Cascabel Desde que escribió este comentario, existe un sitio vi.stackexchange.com.
Flimm

Respuestas:

27

Greplace funciona bien para mí.

También hay una versión lista para patógenos en github.

tuxcanfly
fuente
8
Lo instalé, veo los otros comandos como :Gsearchy :Gbuffersearchexisten, pero cuando escribo :Greplaceobtengo Not an editor command: Greplace.
Ramon Tayag
según los documentos, la sintaxis es: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] por lo que podría hacer una búsqueda recursiva utilizando, por ejemplo::Gsearch -r pattern dir/
vitorbal
Lo que odio de Greplace es que si el patrón está en el nombre del archivo, Greplace falla, porque terminas reemplazándolo también en el nombre del archivo.
Daniel
2
Nada inusual aquí después de 6 años, pero este complemento está muy desactualizado en comparación con algunas respuestas nuevas ( github.com/yegappan/greplace ) última actualización, hace más de 3 años. Podría consultar la solución incorporada de Sid: stackoverflow.com/a/38004355/2224331 (No soy yo en otra cuenta, solo una coincidencia: P)
SidOfc
99

La otra gran opción aquí es simplemente no usar vim:

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

o si tiene alguna forma de generar una lista de archivos, tal vez algo como esto:

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

¡y así!

Cascabel
fuente
¡Gracias! Realmente necesito aprender estas cosas de la línea de comandos de la vieja escuela. Creo que esta es la mejor respuesta. ¿Son estas expresiones regulares expresiones regulares perl o expresiones regulares vim o algún otro tipo de expresión regular?
Eric Johnson
2
@EricJohnson: Son ... sedexpresiones regulares, que son esencialmente expresiones regulares básicas POSIX.2, pero no exactamente (esto está en la página de manual): permiten que \ncoincida con nuevas líneas y algunas otras cosas similares. Por tanto, son prácticamente iguales a greplas expresiones regulares.
Cascabel
¿Hay alguna razón por la que tenga -i en el comando sed? La página de manual dice que es para especificar una extensión, pero no parece que especifiques realmente una.
davekaro
Ok, en realidad parece que 's / pattern / replacement /' es la extensión, creo que ahora lo entiendo.
davekaro
11
En Mac OS X, el único comando sed que funcionó para mí fue:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss
74

EDITAR: use el cfdocomando en lugar de cdopara reducir significativamente la cantidad de comandos que se ejecutarán para lograr esto (porque cdoejecuta comandos en cada elemento mientrascfdo ejecuta comandos en cada archivo)

Gracias al cdocomando agregado recientemente , ahora puede hacer esto en dos comandos simples usando cualquier grepherramienta que haya instalado. ¡No se requieren complementos adicionales !:

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

( cdoejecuta el comando dado para cada término en la lista de corrección rápida, que sugrep comando completa).

(Elimine el cal final del segundo comando si desea reemplazar cada término de búsqueda sin confirmar cada vez)

Sid
fuente
12
Además, puede agregar :cdo updatepara guardar los cambios en todos los archivos.
Zhe Li
1
Pero se detendrá para advertir que no se guarda cada vez antes de cambiar al siguiente búfer cuando se modifica el búfer actual.
Gratis
1
@Zhe gracias, he agregado eso a la respuesta; @lfree Personalmente prefiero confirmar cada cambio, pero puede eliminar el cal final del segundo comando (solo use g) para que busque y reemplace sin pedir confirmación
Sid
1
: cdo% s / término de búsqueda / reemplazar término / g solo reemplaza la primera ocurrencia, pero no funciona para la segunda ocurrencia en mi vim
sunxd
3
para usar update, tuve que:cdo %s/<search term>/<replace term>/g | update
gabe
34

Decidí usar ack y Perl para resolver este problema con el fin de aprovechar las expresiones regulares completas de Perl más poderosas en lugar del subconjunto GNU.

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

Explicación

ack

ACK es una herramienta de línea de comandos impresionante que es una mezcla de grep, findy expresiones regulares de Perl completos (no sólo el subconjunto de GNU). Está escrito en Perl puro, es rápido, tiene resaltado de sintaxis, funciona en Windows y es más amigable para los programadores que las herramientas tradicionales de línea de comandos. Instálelo en Ubuntu con sudo apt-get install ack-grep.

xargs

Xargs es una antigua herramienta de línea de comandos de Unix. Lee elementos de la entrada estándar y ejecuta el comando especificado seguido de los elementos leídos para la entrada estándar. Entonces, básicamente, la lista de archivos generados por ack se agrega al final del perl -pi -E 's/pattern/replacemnt/g'comando.

perl -pi

Perl es un lenguaje de programación. La opción -p hace que Perl cree un bucle alrededor de su programa que itera sobre los argumentos del nombre de archivo. La opción -i hace que Perl edite el archivo en su lugar. Puede modificar esto para crear copias de seguridad. La opción -E hace que Perl ejecute una línea de código especificada como programa. En nuestro caso, el programa es solo una sustitución de expresiones regulares de Perl. Para obtener más información sobre las opciones de la línea de comandos de Perl perldoc perlrun. Para obtener más información sobre Perl, consulte http://www.perl.org/ .

Eric Johnson
fuente
Me gusta esta respuesta porque funciona incluso con los finales de línea de cygwin. Tenga en cuenta que agrega a \ r al final de las líneas modificadas, pero cuando usa sed, reemplazará cada nueva línea, haciendo que sus diferencias sean inutilizables. Perl es un poco más cuerdo, y esta es una frase muy buena para buscar y reemplazar. ¡Gracias!
andrew
@andrew, no estoy seguro de qué va mal en tu caso. ¿Le ayudaría a usar \ R en lugar de \ n en sus expresiones regulares? Consulte la sección \ R en perldoc.perl.org/perlrebackslash.html#Misc . Pruebe también perldoc.perl.org/perlre.html#Regular-Expressions . Perl es extremadamente multiplataforma y estoy seguro de que hay una manera de no terminar con finales de línea destrozados.
Eric Johnson
Mi expresión regular no tiene líneas nuevas, las versiones cygwin de estos programas agregan automáticamente \ r al final de cada línea modificada. Me gustó específicamente la versión de perl porque solo modifica las líneas que coincide, mientras que sed modificará todas las líneas, incluso si no coinciden con la expresión regular.
andrew
¿Qué pasa si la ruta del archivo contiene espacios?
shengy
23

tal vez haga esto:

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

Explicación:

  • :noautocmd vim /Search/ **/*⇒ buscar ( vimes una abreviatura de vimgrep) patrón en todos los archivos en todos los subdirectorios de cwd sin activar autocmds ( :noautocmd), por el bien de la velocidad.
  • :set hidden ⇒ permitir que los búferes modificados no se muestren en una ventana (podría estar en su vimrc)
  • :cfirst ⇒ saltar al primer resultado de búsqueda
  • qa ⇒ comenzar a grabar una macro en el registro a
  • :%s//Replace/gce⇒ reemplace todas las apariciones del último patrón de búsqueda (aún /Search/en ese momento) con Replace:
    • varias veces en una misma línea ( gbandera)
    • con confirmación de usuario ( cbandera)
    • sin error si no se encuentra ningún patrón ( ebandera)
  • :cnf⇒ saltar al siguiente archivo en la lista creada por el vimcomando
  • q ⇒ detener la grabación de la macro
  • 1000@a ⇒ reproducir la macro almacenada en el registro 1000 veces
  • :wa ⇒ guardar todos los búferes modificados

* EDITAR * Vim 8 vías:

Comenzando con Vim 8, hay una mejor manera de hacerlo, ya que se :cfdorepite en todos los archivos de la lista de corrección rápida:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa
Benoit
fuente
es posible que desee explicarlo un poco más para nosotros, los mortales menores. No me queda claro si necesita hacer esta "secuencia" cada vez. Estoy haciendo eso más rápido usando mi viejo Delphi 5 oxidado, así que seguramente me debe estar perdiendo algo.
Lieven Keersmaekers
1
@Lieven: Tienes razón, esto es un poco oscuro sin comentarios. Desarrollaré alguna explicación.
Benoit
+1 pero aunque vimme ha ganado por muchas cosas, creo que me quedaré con PowerGrep con esto.
Lieven Keersmaekers
Gracias por tu respuesta y la explicación de todos los comandos, te lo agradezco mucho. Acepto la otra respuesta porque prefiero usar un complemento para esto.
psyho
He marcado esto porque causa más confusión, dada la disparidad entre el nivel de alguien que hace la pregunta y el nivel requerido para entender esta respuesta.
Lloyd Moore
22

Poblar :args desde un comando de shell

Es posible (en algunos sistemas operativos 1 )) proporcionar los archivos para:args través de un comando de shell.

Por ejemplo, si tiene instalado ack 2 ,

:args `ack -l pattern`

le pedirá a ack que devuelva una lista de archivos que contengan 'patrón' y los ponga en la lista de argumentos.

O con grep simple, supongo que sería:

:args `grep -lr pattern .`  


Luego puede usarlo :argdocomo lo describe el OP:

:argdo %s/pattern/replacement/gce


Rellenar :argsde la lista quickfix

Consulte también la respuesta de nelstrom a una pregunta relacionada que describe un comando simple definido por el usuario que llena el arglist de la lista de correcciones rápidas actual. Esto funciona muy bien con muchos comandos y complementos cuya salida termina en la lista de arreglos rápidos ( :vimgrep, :Ack3 , :Ggrep4 ).

La secuencia para realizar una búsqueda en todo el proyecto se podría realizar con:

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

donde :Qargses la llamada al comando definido por el usuario que llena el arglist de la lista de arreglos rápidos.

También encontrará enlaces en la siguiente discusión. a complementos simples que reducen este flujo de trabajo a 2 o 3 comandos.

Enlaces

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim : github.com/mileszs/ack.vim
  4. fugitivo - github.com/tpope/vim-fugitive
exbinario
fuente
1
argdo se detiene después del primer archivo con un problema de escritura
frankster
9

Una opción más en 2016, el complemento far.vim :

far.vom

Oleg Khalidov
fuente
4
También debe decir que es el autor del complemento :)
sitilge
5

Si no le importa introducir dependencia externa, he elaborado un complemento ctrlsf.vim (depende de ack o ag ) para hacer el trabajo.

Puede formatear y mostrar el resultado de la búsqueda de ack / ag, y sincronizar los cambios en el búfer de resultados con los archivos reales del disco.

Tal vez la siguiente demostración explique más

ctrlsf_edit_demo

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

El paso 1 completa la lista de arreglos rápidos con los elementos que desea. En este caso, se propaga con los términos de búsqueda que desea cambiar a través de grep.

cfdoejecuta el siguiente comando en cada archivo de la lista de corrección rápida. Escriba :help cfdopara obtener más detalles.

s/<search term>/<replace term>/greemplaza cada término. /gsignifica reemplazar cada ocurrencia en el archivo.

| update guarda el archivo después de cada reemplazo.

Reuní todo esto en base a esta respuesta y sus comentarios, pero sentí que merecía su propia respuesta ya que está todo en un solo lugar.

Kyle Heironimus
fuente
Para mí en NeoVim, se necesitaba hacer una pequeña modificación en la línea 2 anterior: 2. :cfdo %s/<search term>/<replace term>/g | updatesin el %, solo se reemplaza la primera aparición del patrón.
Kareem Ergawy
Gracias Kareem. Actualicé la respuesta ya que también %sfunciona en vim regular.
Kyle Heironimus
0

Básicamente, quería el reemplazo en un solo comando y, lo que es más importante, dentro de vim.

Basado en la respuesta de @Jefromi, he creado un atajo de teclado, que había configurado en mi archivo .vimrc como este

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

ahora desde el vim, con un golpe de líder + r obtengo el comando cargado en vim, que edito como a continuación,

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

Por lo tanto, hago el reemplazo con un solo comando y, lo que es más importante, dentro de vim.

deepak
fuente
y de hecho uso ag en lugar de grep
deepak