Reemplazar cadenas en archivos según ciertos criterios de búsqueda es una tarea muy común. ¿Cómo puedo?
- reemplazar cadena
fooconbaren todos los archivos del directorio actual? - hacer lo mismo recursivamente para subdirectorios?
- ¿reemplazar solo si el nombre del archivo coincide con otra cadena?
- ¿reemplazar solo si la cadena se encuentra en un contexto determinado?
- ¿reemplazar si la cadena está en un determinado número de línea?
- reemplace varias cadenas con el mismo reemplazo
- reemplazar múltiples cadenas con diferentes reemplazos
Respuesta aceptada:
1. Reemplazando todas las apariciones de una cadena con otra en todos los archivos en el directorio actual:
Estos son para casos en los que sabe que el directorio contiene solo archivos normales y que desea procesar todos los archivos no ocultos. Si ese no es el caso, use los enfoques en 2.
Todo sed las soluciones en esta respuesta asumen GNU sed . Si usa FreeBSD o macOS, reemplace -i con -i '' . También tenga en cuenta que el uso de -i cambiar con cualquier versión de sed tiene ciertas implicaciones para la seguridad del sistema de archivos y no es aconsejable en ningún script que planee distribuir de alguna manera.
-
Archivos no recursivos, solo en este directorio:
sed -i -- 's/foo/bar/g' * perl -i -pe 's/foo/bar/g' ./*
(el perl uno fallará para los nombres de archivo que terminan en | o espacio)).
-
Archivos regulares recursivos (incluidos los ocultos ) en este y todos los subdirectorios
find . -type f -exec sed -i 's/foo/bar/g' {} +Si está utilizando zsh:
sed -i -- 's/foo/bar/g' **/*(D.)(puede fallar si la lista es demasiado grande, consulte
zargspara evitarlo).Bash no puede buscar archivos regulares directamente, se necesita un bucle (las llaves evitan configurar las opciones globalmente):
( shopt -s globstar dotglob; for file in **; do if [[ -f $file ]] && [[ -w $file ]]; then sed -i -- 's/foo/bar/g' "$file" fi done )Los archivos se seleccionan cuando son archivos reales (-f) y se pueden escribir (-w).
2. Reemplace solo si el nombre del archivo coincide con otra cadena/tiene una extensión específica/es de cierto tipo, etc.:
-
Archivos no recursivos, solo en este directorio:
sed -i -- 's/foo/bar/g' *baz* ## all files whose name contains baz sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz -
Archivos regulares recursivos en este y todos los subdirectorios
find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +Si está utilizando bash (las llaves evitan configurar las opciones globalmente):
( shopt -s globstar dotglob sed -i -- 's/foo/bar/g' **baz* sed -i -- 's/foo/bar/g' **.baz )Si está utilizando zsh:
sed -i -- 's/foo/bar/g' **/*baz*(D.) sed -i -- 's/foo/bar/g' **/*.baz(D.)
El -- sirve para decirle a sed que no se darán más banderas en la línea de comando. Esto es útil para proteger contra los nombres de archivo que comienzan con - .
-
Si un archivo es de cierto tipo, por ejemplo, ejecutable (ver
man findpara más opciones):find . -type f -executable -exec sed -i 's/foo/bar/g' {} +
zsh :
sed -i -- 's/foo/bar/g' **/*(D*)
3. Reemplace solo si la cadena se encuentra en un contexto determinado
-
Reemplazar
fooconbarsolo si hay unbazmás adelante en la misma línea:sed -i 's/foo(.*baz)/bar1/' file
En sed , usando ( ) guarda lo que está entre paréntesis y luego puede acceder a él con 1 . Hay muchas variaciones de este tema, para obtener más información sobre estas expresiones regulares, consulta aquí.
-
Reemplazar
fooconbarsolo sifoose encuentra en la columna 3d (campo) del archivo de entrada (asumiendo campos separados por espacios en blanco):gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file
(necesita gawk 4.1.0 o posterior).
-
Para un campo diferente, simplemente use
$NdondeNes el número del campo de interés. Para un separador de campo diferente (:en este ejemplo) use:gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file
Otra solución usando perl :
perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo
NOTA:tanto el awk y perl las soluciones afectarán el espacio en el archivo (elimine los espacios en blanco iniciales y finales, y convierta las secuencias de espacios en blanco en un carácter de espacio en aquellas líneas que coincidan). Para un campo diferente, use $F[N-1] donde N es el número de campo que desea y para usar un separador de campo diferente (el $"=":" establece el separador de campo de salida en : ):
perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo
-
Reemplazar
fooconbarsólo en la 4ª línea:sed -i '4s/foo/bar/g' file gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file perl -i -pe 's/foo/bar/g if $.==4' file
4. Múltiples operaciones de reemplazo:reemplazar con diferentes cadenas
-
Puedes combinar
sedcomandos:sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
Tenga en cuenta que el orden importa (sed 's/foo/bar/g; s/bar/baz/g' sustituirá foo con baz ).
-
o comandos Perl
perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file -
Si tiene una gran cantidad de patrones, es más fácil guardar sus patrones y sus reemplazos en un
sedarchivo de secuencia de comandos:#! /usr/bin/sed -f s/foo/bar/g s/baz/zab/g -
O bien, si tiene demasiados pares de patrones para que lo anterior sea factible, puede leer los pares de patrones de un archivo (dos patrones separados por espacios, $patrón y $reemplazo, por línea):
while read -r pattern replacement; do sed -i "s/$pattern/$replacement/" file done < patterns.txt -
Eso será bastante lento para largas listas de patrones y grandes archivos de datos, por lo que es posible que desee leer los patrones y crear un
sedguión de ellos en su lugar. Lo siguiente asume un <espacio> el delimitador separa una lista de COINCIDIR<espacio>REEMPLAZAR pares que ocurren uno por línea en el archivopatterns.txt:sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt | sed -f- ./editfile >outfile
El formato anterior es en gran medida arbitrario y, por ejemplo, no permite un <espacio> en cualquiera de PARTIDO o REEMPLAZAR . Sin embargo, el método es muy general:básicamente, si puede crear un flujo de salida que se parece a un sed secuencia de comandos, entonces puede generar esa transmisión como un sed secuencia de comandos especificando sed archivo de script como - estándar.
-
Puede combinar y concatenar varios scripts de manera similar:
SOME_PIPELINE | sed -e'#some expression script' -f./script_file -f- -e'#more inline expressions' ./actual_edit_file >./outfile
Un POSIX sed concatenará todos los scripts en uno en el orden en que aparecen en la línea de comandos. Ninguno de estos necesita terminar en un n nueva línea.
-
greppuede funcionar de la misma manera:sed -e'#generate a pattern list' <in | grep -f- ./grepped_file -
Cuando se trabaja con cadenas fijas como patrones, es una buena práctica escapar de los metacaracteres de expresiones regulares. . Puedes hacer esto con bastante facilidad:
sed 's/[]$&^*./[]/\&/g s| *([^ ]*) *([^ ]*).*|s/1/2/g| ' <patterns.txt | sed -f- ./editfile >outfile
5. Múltiples operaciones de reemplazo:reemplaza múltiples patrones con la misma cadena
-
Reemplace cualquiera de
foo,barobazconfoobarsed -Ei 's/foo|bar|baz/foobar/g' file -
o
perl -i -pe 's/foo|bar|baz/foobar/g' file