
Usando un shell como bash o zshell, ¿cómo puedo hacer un 'buscar y reemplazar' recursivo? En otras palabras, quiero reemplazar cada aparición de 'foo' con 'bar' en todos los archivos de este directorio y sus subdirectorios.
Respuesta1
Este comando lo hará (probado tanto en Mac OS X Lion como en Kubuntu Linux).
# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'
Así es como funciona:
find . -type f -name '*.txt'
busca, en el directorio actual (.
) y debajo, todos los archivos normales (-type f
) cuyos nombres terminan en.txt
|
pasa la salida de ese comando (una lista de nombres de archivos) al siguiente comandoxargs
recoge esos nombres de archivos y los entrega uno por uno ased
sed -i '' -e 's/foo/bar/g'
significa "editar el archivo en el lugar, sin una copia de seguridad, y realizar la siguiente sustitución (s/foo/bar
) varias veces por línea (/g
)" (verman sed
)
Tenga en cuenta que la parte "sin copia de seguridad" en la línea 4 está bien para mí, porque los archivos que estoy cambiando están bajo control de versiones de todos modos, por lo que puedo deshacerlos fácilmente si hubo un error.
Para evitar tener que recordar esto, utilizo un script bash interactivo, de la siguiente manera:
#!/bin/bash
# find_and_replace.sh
echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"
find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"
Respuesta2
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +
Esto elimina la xargs
dependencia.
Respuesta3
Si estás usando Git, puedes hacer esto:
git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
-l
enumera sólo nombres de archivos. -z
imprime un byte nulo después de cada resultado.
Terminé haciendo esto porque algunos archivos en un proyecto no tenían una nueva línea al final del archivo, y sed agregó una nueva línea incluso cuando no realizó otros cambios. (No hay comentarios sobre si los archivos deben tener una nueva línea al final.
Respuesta4
Aquí está mi función zsh/perl que uso para esto:
change () {
from=$1
shift
to=$1
shift
for file in $*
do
perl -i.bak -p -e "s{$from}{$to}g;" $file
echo "Changing $from to $to in $file"
done
}
Y lo ejecutaría usando
$ change foo bar **/*.java
(Por ejemplo)