2009-05-25 18 views
53

Necesito hacer un regex buscar y reemplazar en todos los archivos en una carpeta (y sus subcarpetas). ¿Cuál sería el comando linux shell para hacer eso?sed beginner: cambiar todas las ocurrencias en una carpeta

Por ejemplo, quiero ejecutar esto sobre todos los archivos y sobrescribir el archivo anterior con el nuevo texto reemplazado.

sed 's/old text/new text/g' 
+0

http://theunixshell.blogspot.com/2012/12/find-and-replace-string-in-all-files.html – Vijay

Respuesta

82

No hay manera de hacerlo utilizando sólo sed. Tendrá que usar al menos la utilidad hallazgo juntos:

find . -type f -exec sed -i.bak "s/foo/bar/g" {} \; 

Este comando creará un archivo .bak para cada archivo cambiado.

Notas:

  • El argumento -i para sed orden es una extensión de GNU, por lo que, si se ejecuta este comando con el BSD de sed tendrá que redirigir la salida a un archivo nuevo a continuación, cambiar el nombre eso.
  • La utilidad find no implementa el argumento -exec en cuadros antiguos de UNIX, por lo tanto, deberá usar un | xargs.
0

podría sugerir (tras realizar una copia de seguridad de archivos):

find /the/folder -type f -exec sed -ibak 's/old/new/g' {} ';' 
-3

Podría intentar my mass search/replace Perl script. Tiene algunas ventajas sobre las soluciones de utilidad encadenadas (como no tener que lidiar con múltiples niveles de interpretación de metacarácter de shell).

5

Para la portabilidad, no confío en las características de sed que son específicas de Linux o BSD. En su lugar, utilizo el script overwrite del libro de Kernighan y Pike sobre el Entorno de programación de Unix.

El comando es entonces

find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';' 

Y el guión overwrite (que uso todo el lugar) es

#!/bin/sh 
# overwrite: copy standard input to output after EOF 
# (final version) 

# set -x 

case $# in 
0|1)  echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2 
esac 

file=$1; shift 
new=/tmp/$$.new; old=/tmp/$$.old 
trap 'rm -f $new; exit 1' 1 2 15 # clean up files 

if "[email protected]" >$new    # collect input 
then 
    cp $file $old # save original file 
    trap 'trap "" 1 2 15; cp $old $file  # ignore signals 
      rm -f $new $old; exit 1' 1 2 15 # during restore 
    cp $new $file 
else 
    echo "overwrite: $1 failed, $file unchanged" 1>&2 
    exit 1 
fi 
rm -f $new $old 

La idea es que se sobrescribe un archivo sólo si el comando se ejecuta correctamente. Útil en find y también en los que no se desea utilizar

sed 's/old/new/g' file > file # THIS CODE DOES NOT WORK 

porque el shell trunca el archivo antes de sed puede leerlo.

20

Prefiero usar find | xargs cmd sobre find -exec porque es más fácil de recordar.

Este ejemplo reemplaza a nivel mundial "foo" con la "barra" en archivos .txt en o por debajo de su directorio actual:

find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g" 

Los -print0 y -0 opciones se pueden omitir si los nombres de archivo no contienen caracteres extraños como espacios.

+1

Si estás en OSX, intente 'find. -type f -name "* .txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g" '(note que proporciona una cadena vacía al argumento' -i'). – jkukul

-4

En caso de que el nombre de los archivos en la carpeta tenga algunos nombres comunes (como archivo1, archivo2 ...) que he utilizado para el ciclo.

for i in {1..10000..100}; do sed 'old\new\g' 'file'$i.xml > 'cfile'$i.xml; done 
+0

esto no está relacionado con la pregunta hecha. La pregunta no menciona nada sobre el mismo patrón de nombre de archivo/carpeta. Por favor evite tales respuestas –

Cuestiones relacionadas