2010-01-21 8 views
32

me enteré de comando sustituto de Vim ...¿Cómo puedo buscar y reemplazar recursivamente en un directorio en Vim?

:%s/replaceme/replacement/gi 

Y vimgrep ...

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

¿Hay una manera de combinarlos con el fin de hacer un reemplazo a través de todos los archivos bajo una ¿directorio?

+0

[Aquí] (http: // stackoverflow.com/a/13944042/834176) es una forma de buscar y reemplazar recursivamente, pero solo en archivos bajo control de versión (podría generalizarse fácilmente si realmente desea buscar * todos * archivos bajo un directorio). –

Respuesta

47

Aunque soy un usuario de Vim, generalmente uso find y sed para este tipo de cosas. La sintaxis de expresiones regulares para sed es similar a la de Vim (ambos son descendientes de ed), aunque no idénticos. Con GNU sed también puede usar -i para ediciones en el lugar (normalmente sed emite la versión modificada a stdout).

Por ejemplo:

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

Pieza por pieza:

  • proj = búsqueda en el árbol "proj"
  • -name '*.rb' = cosas cuyos nombres coinciden con '* .rb'
  • -type f = y son archivos regulares (no directorios, tuberías, enlaces simbólicos, etc.)
  • -exec sed = ejecutar sed con estos argumentos:
    • -i = con en el lugar edita
    • -e 's/regex/replacement/g' = ejecutar un comando "sustituto" (que casi idéntica a Vim de :s, pero tenga en cuenta la falta de % - que es el valor por defecto en sed)
    • -- = final de banderas, nombres de archivo comienzan aquí
    • {} = esta dice que find th e nombres de archivo que se ha encontrado se deben colocar en la línea de comandos de sed aquí
    • + = dice esto find que se terminó la acción -exec, y queremos agrupar los argumentos en el menor número sed invocaciones como sea posible (por lo general más eficientes que correr una vez por nombre de archivo). Para ejecutarlo una vez por nombre de archivo, puede usar \; en lugar de +.

Esta es la solución general con GNU sed y find. Esto se puede acortar un poco en casos especiales. Por ejemplo, si sabe que su patrón de nombre no coincidirá con ningún directorio, puede omitir -type f. Si sabe que ninguno de sus archivos comienza con - puede omitir --. Here's an answer I posted to another question with more details on passing filenames found with find to other commands.

+6

Asegúrese y pruebe su 'encontrar' antes de dársela a' sed'; esto puede ser realmente malo si es excesivamente incluyente. También es una buena idea darle a 'sed' la opción' -c' ('--copy'), lo que evitará que se meta con archivos de solo lectura. – Cascabel

+0

@BroSlow Bastante suficiente. He actualizado la respuesta para ser más seguro/más general. (Dicho esto, en Linux al menos ARG_MAX es bastante grande, y para el caso de uso descrito en la pregunta, los nombres de archivo con espacios o caracteres especiales serían bastante extraños). –

+0

Si usa este método no puede usar la conformación porque sed no lo admite, puede usar 'vim -c '%/regex/replacement/gc' -c 'wq' - {} \;' de esta forma usted puede confirmar sus cambios también – Funonly

4

Hay un plugin que intenta hacer precisamente eso: Vimgrep Replace.

Oh, espera ... hay otros: Global Replace, EasyGrep.

Para una solución de ningún plug-in, tal vez argdo sería de ayuda si usted podría conseguir vimgrep 's de salida en la lista de argumentos (que puede ser ajustado con args), pero me parece que no puede averiguar los detalles. Estaría feliz si alguien tomara la idea y la mejorara ... así que aquí está.

La idea básica detrás del primer complemento (supongo que los otros también ...) es usar primero vimgrep, luego recorrer las coincidencias con :cnext y aplicar el comando de sustitución en cada línea. Una función para lograr esto podría ser lo suficientemente pequeña como para ponerlo en .vimrc. (¿Quizás puede levantar uno de las fuentes de los complementos?)

(Supongo que hgimenez podría estar buscando una solución, pero si es apropiado o no, probablemente dependería de la cantidad de archivos que se procesen ... Los complementos debería estar bien independientemente).

+0

Gracias, pero arrrgh! ¿Hay alguna manera de hacerlo sin instalar otro complemento #! * &%? – Ethan

+0

@Ethan: lo siento por ti. :-) Creo que debe haber una forma que involucre: args,: argdo,: copen o lo que sea ... Simplemente no uso vimgrep lo suficiente como para haber pensado mucho sobre eso antes y no puedo ser capaz de armarlo la solución perfecta para ti por el momento. Pero todavía lo intento, así que tal vez se me ocurra algo y edite. –

+0

Oh, bueno ... Realmente parece que no puedo encontrar una solución limpia y simple sin complementos o definitivamente me aseguro de que no hay ninguno. ¡Mi Vim-fu me está fallando! :-(Los complementos parecen agradables, quizás valdría la pena intentarlo ... –

4

Una forma sería abrir todos los archivos en los que desea ejecutar la sustitución (o cualquier comando), y ejecutar :bufdo <cmd>, que ejecutará <cmd> en todos los búferes abiertos.

7

Usted podría funcionar vimgrep continuación, grabar una macro que va a cada línea en la salida vimgrep y hace lo sustituya, algo como esto: (! No te tipo dentro de los # comentarios)

: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 

No he probado así que puede necesitar un pequeño ajuste: pruébelo en algunos archivos para que no le importe que lo arruinen primero.

La ventaja de este exceso de sed es que Vim regex son más potentes, y puede agregar la opción c a: s para verificar cada reemplazo.

Editar: Modifiqué mi publicación original porque era incorrecta. Estaba usando: 1vimgrep para encontrar la primera coincidencia en cada archivo, pero leí mal los documentos, solo encuentra una coincidencia en todos los archivos.

+0

¡Genial! En la búsqueda de una solución de expresión única, nunca dejé de considerar una macro ... Me hace sentir un poco tonto ahora. +1. :-) –

+0

¡Me acabas de ahorrar horas de trabajo, gracias! : D Necesitaba copiar un valor en un archivo xml a otra parte del archivo, luego minúsculas, en más de 200 archivos. – Jason

45

args y argdo debe hacer lo que necesita, p.

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

Ver this page.

+0

¡Gracias! Es genial :) – starikovs

+1

Whoa vimskill ++ –

+1

años de sufrimiento y cuestionamiento elección de carrera terminó hoy –

0

Esto funciona para una pequeña cantidad de archivos.

vim `encontrar. -type f `

: bufdo% s/pattern/replacement/gec | actualización

Cuestiones relacionadas