Cómo grep y reemplazar

252

Necesito buscar recursivamente una cadena especificada dentro de todos los archivos y subdirectorios dentro de un directorio y reemplazar esta cadena con otra cadena.

Sé que el comando para encontrarlo podría verse así:

grep 'string_to_find' -r ./*

Pero, ¿cómo puedo reemplazar cada instancia de string_to_findcon otra cadena?

billtian
fuente
No creo que grep pueda hacer esto (podría estar equivocado). Las formas más fáciles serían usar sed o perl para hacer el reemplazo
Memento Mori
2
Trate de usarsed -i 's/.*substring.*/replace/'
Eddy_Em
2
@Eddy_Em Eso reemplazará toda la línea con reemplazar. Debe usar la agrupación para capturar la parte de la línea antes y después de la subcadena y luego colocarla en la línea de reemplazo. sed -i 's/\(.*\)substring\(.*\)/\1replace\2/'
JStrahl

Respuestas:

249

Otra opción es usar find y luego pasarlo a través de sed.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;
rezizter
fuente
34
En el terminal OS X 10.10, -ise requiere una cadena de extensión adecuada para el parámetro . Por ejemplo, de find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" {} \;todos modos, proporcionar una cadena vacía aún crea un archivo de copia de seguridad diferente al descrito en el manual ...
Eonil
10
¿Por qué obtengo "sed: RE error: secuencia de bytes ilegal". Y sí, agregué el -i ""para OS X. Funciona de otra manera.
taco
2
Tuve el problema ilegal de la secuencia de bytes en macOS 10.12, y esta pregunta / respuesta resolvió mi problema: stackoverflow.com/questions/19242275/… .
abeboparebop
3
Esto toca cada archivo, por lo que se modifican los tiempos de archivo; y convierte terminaciones de línea de CRLFa LFen Windows.
jww
183

Tengo la respuesta

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'
billtian
fuente
14
Esto exploraría los archivos coincidentes dos veces ... una vez con grepy luego otra vez con sed. Usar el findmétodo es más eficiente, pero este método que mencionas funciona.
cmevoli
41
En OS X, deberá cambiar sed -i 's/str1/str2/g'a sed -i "" 's/str1/str2/g'para que esto funcione.
jdf
66
@cmevoli con este método, revisa greptodos los archivos y sedsolo escanea los archivos que coinciden grep. Con el findmétodo en la otra respuesta, findprimero enumera todos los archivos y luego sedexplorará todos los archivos en ese directorio. Por lo tanto, este método no es necesariamente más lento, depende de cuántas coincidencias hay y las diferencias en las velocidades de búsqueda entre sed, grepy find.
joelostblom
44
OTOH de esta manera le permite VISUALIZAR qué encuentra grep ANTES de reemplazarlo realmente, reduciendo en gran medida el riesgo de falla, especialmente para expresiones regulares n00bs como yo
Lennart Rolland
2
Esto también es útil cuando su reemplazo de grep es más inteligente que sed. Por ejemplo, ripgrep obedece .gitignore mientras que sed no.
user31389
43

Incluso podrías hacerlo así:

Ejemplo

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

Esto buscará la cadena ' windows ' en todos los archivos relativos al directorio actual y reemplazará ' windows ' con ' linux ' para cada aparición de la cadena en cada archivo.

Dulith De Costa
fuente
2
El grepsolo es útil si hay archivos que no deben modificarse. La ejecución seden todos los archivos actualizará la fecha de modificación del archivo, pero dejará el contenido sin cambios si no hay coincidencias.
tripleee
@tripleee: Tenga cuidado con ... pero [sed] deje el contenido sin cambios si no hay coincidencias " . Al usarlo -i, creo que sedcambia el tiempo de archivo de cada archivo que toca, a pesar de que el contenido no cambia. sedTambién convierte las terminaciones de línea . yo no uso seden Windows en un repositorio git porque todos CRLFse cambian a LF.
JWW
Este comando necesita un "" después de -i para indicar que no se realizarán archivos de copia de seguridad después de que tenga lugar la sustitución in situ, al menos en macosx. Consulte la página del manual para más detalles. Si desea una copia de seguridad, aquí es donde coloca la extensión del archivo que se va a crear.
spinyBabbler
31

Esto funciona mejor para mí en OS X:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

Fuente: http://www.praj.com.au/post/23691181208/grep-replace-text-string-in-files

Marc Juchli
fuente
¡esto es perfecto! también funciona con ag:ag "search" -l -r . | sort | uniq | xargs perl -e 's/search/replace' -pi
@sebastiankeller Su comando Perl carece de la barra diagonal final, que es un error de sintaxis.
tripleee
3
¿Por qué es la sort -uparte par de esto? ¿En qué circunstancias esperaría grep -rlproducir el mismo nombre de archivo dos veces?
tripleee
5

Otras soluciones mezclan sintaxis de expresiones regulares. Para usar patrones perl / PCRE para tanto buscar y reemplazar, y sólo los archivos coincidentes del proceso, esto funciona bastante bien:

grep -rlZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

dónde match1 y match2generalmente son idénticos, pero match1pueden simplificarse para eliminar características más avanzadas que solo son relevantes para la sustitución, por ejemplo, capturar grupos.

Traducción: grep recursivamente y enumere los archivos que coinciden con este patrón PCRE, separados por nul para proteger cualquier carácter especial en el nombre del archivo, luego canalice los nombres de archivo a los xargsque se espera una lista separada por nul, pero no hará nada si no se reciben nombres, y llegar perla sustituir líneas donde se encuentran coincidencias.

Agregue el Iinterruptor greppara ignorar los archivos binarios. Para la coincidencia entre mayúsculas y minúsculas, suelte el iinterruptor grepy la ibandera adjunta a la expresión de sustitución, pero no el iinterruptor en perlsí.

Walf
fuente
Perl en sí mismo es bastante capaz de recurrir una estructura de archivos. De hecho, hay una herramienta find2perlque se envía con Perl que hace este tipo de cosas sin ningún xargstruco.
tripleee
@tripleee findno busca el contenido del archivo, y el punto es procesar solo los archivos coincidentes sin escribir un programa Perl.
Walf
Esta es una buena solución para Windows, ya que evita el problema de las soluciones basadas en sed de convertir las terminaciones de línea. ¡Gracias!
JamHandy
4

Por lo general, no con grep, sino con sed -i 's/string_to_find/another_string/g'o perl -i.bak -pe 's/string_to_find/another_string/g'.

minopret
fuente
3

¡Tenga mucho cuidado al usar findy seden un repositorio de git! Si no excluye los archivos binarios, puede terminar con este error:

error: bad index file sha1 signature 
fatal: index file corrupt

Para resolver este error, debe revertirlo sedreemplazando su new_stringcon suold_string . Esto revertirá las cadenas reemplazadas, por lo que volverá al comienzo del problema.

La forma correcta de buscar una cadena y reemplazarla es omitir findy usar grepen su lugar para ignorar los archivos binarios:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

Créditos para @hobs

tsveti_iko
fuente
1

Esto es lo que haría:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

Esto buscará todos los archivos que contengan filenameel nombre del archivo debajo de /path/to/dir, para cada archivo encontrado, busque la línea con searchstringy reemplace oldcon new.

Sin embargo, si desea omitir la búsqueda de un archivo específico con una filenamecadena en el nombre del archivo, simplemente haga lo siguiente:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

Esto hará lo mismo arriba, pero para todos los archivos encontrados debajo /path/to/dir.

tinnick
fuente
0

Otra opción sería simplemente usar perl con globstar.

La activación shopt -s globstaren su .bashrc(o donde sea) permite que el **patrón global coincida con todos los subdirectorios y archivos de forma recursiva.

Por lo tanto, el uso perl -pXe 's/SEARCH/REPLACE/g' -i **se reemplazará recursivamente SEARCHcon REPLACE.

La -Xbandera le dice a Perl que "desactive todas las advertencias", lo que significa que no se quejará de los directorios.

El Globstar también le permite hacer cosas como sed -i 's/SEARCH/REPLACE/g' **/*.extsi quisiera sustituir SEARCHcon REPLACEen todos los archivos de los niños con la extensión .ext.

CulpableDolphin
fuente
"Otra opción sería usar Perl con Globstar ..." - No en máquinas Posixy, como Solaris. Es por eso que estoy buscando específicamente grepy sed.
jww