bash - sustituir - ¿Cómo hacer una búsqueda/sustitución recursiva de una cadena con awk o sed?
sed sustituir una linea (30)
¿Cómo puedo encontrar y reemplazar cada aparición de:
subdomainA.example.com
con
subdomainB.example.com
en cada archivo de texto bajo el árbol de directorios /home/www/
recursivamente?
Aquí hay una versión que debería ser más general que la mayoría; no requiere find
(usando du
lugar), por ejemplo. Requiere xargs
, que solo se encuentran en algunas versiones de Plan 9 (como 9front).
du -a | awk -F'' '' ''{ print $2 }'' | xargs sed -i -e ''s/subdomainA/.example/.com/subdomainB.example.com/g''
Si quieres agregar filtros como extensiones de archivo usa grep
:
du -a | grep "/.scala$" | awk -F'' '' ''{ print $2 }'' | xargs sed -i -e ''s/subdomainA/.example/.com/subdomainB.example.com/g''
Esta es la mejor solución que he encontrado para OSX y Windows (msys2). Debería funcionar con cualquier cosa que pueda obtener la versión gnu de sed. Omite los directorios .git para que no dañe sus sumas de comprobación.
En mac, simplemente instale coreutils primero y asegúrese de que gsed esté en la ruta -
brew install coreutils
Luego pego esta función en mi zshrc / bashrc ->
replace-recursive() {
hash gsed 2>/dev/null && local SED_CMD="gsed" || SED_CMD="sed"
find . -type f -name "*.*" -not -path "*/.git/*" -print0 | xargs -0 $SED_CMD -i "s/$1/$2/g"
}
usage: replace-recursive <find> <replace>
Este es compatible con los repositorios de git, y un poco más simple:
Linux:
git grep -l ''original_text'' | xargs sed -i ''s/original_text/new_text/g''
Mac:
git grep -l ''original_text'' | xargs sed -i '''' -e ''s/original_text/new_text/g''
(Gracias a http://blog.jasonmeridth.com/posts/use-git-grep-to-replace-strings-in-files-in-your-git-repository/ )
La forma más sencilla para mí es
grep -rl oldtext . | xargs sed -i ''s/oldtext/newtext/g''
Para Qshell (qsh) en IBMi, no bash como está etiquetado por OP.
Limitaciones de los comandos qsh:
- Encontrar no tiene la opción -print0
- xargs no tiene opción -0
- sed no tiene opción -i
Así la solución en qsh:
PATH=''your/path/here''
SEARCH=/'subdomainA.example.com/'
REPLACE=/'subdomainB.example.com/'
for file in $( find ${PATH} -P -type f ); do
TEMP_FILE=${file}.${RANDOM}.temp_file
if [ ! -e ${TEMP_FILE} ]; then
touch -C 819 ${TEMP_FILE}
sed -e ''s/''$SEARCH''/''$REPLACE''/g'' /
< ${file} > ${TEMP_FILE}
mv ${TEMP_FILE} ${file}
fi
done
Advertencias:
- La solución excluye el manejo de errores
- No Bash como etiquetado por OP
Para cualquiera que use Silver Searcher ( ag
)
ag SearchString -l0 | xargs -0 sed -i ''s/SearchString/Replacement/g''
Dado que ag ignora los archivos / carpetas git / hg / svn de forma predeterminada, es seguro ejecutar esto dentro de un repositorio.
Para mí, la solución más fácil de recordar es https://.com/a/2113224/565525 , es decir:
sed -i '''' -e ''s/subdomainA/subdomainB/g'' $(find /home/www/ -type f)
NOTA : -i ''''
resuelve el problema OSX sed: 1: "...": invalid command code .
NOTA : Si hay demasiados archivos para procesar, obtendrás la Argument list too long
. La solución: use la solución find -exec
o find -exec
descrita anteriormente.
Para reducir los archivos a los que se puede sed
recursiva, podría grep
para su instancia de cadena:
grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
Si ejecuta man grep
, notará que también puede definir una --exlude-dir="*.git"
si desea omitir la búsqueda en los directorios .git, evitando los problemas del índice de git como otros lo han señalado cortésmente.
Guiándote a
grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
Para reemplazar todas las ocurrencias en un repositorio git puede usar:
git ls-files -z | xargs -0 sed -i ''s/subdomainA/.example/.com/subdomainB.example.com/g''
Ver la lista de archivos en el repositorio de git local? para otras opciones para listar todos los archivos en un repositorio. Las opciones -z
le indican a git que separe los nombres de los archivos con un byte cero, lo que garantiza que xargs
(con la opción -0
) pueda separar los nombres de los archivos, incluso si contienen espacios o lo que sea.
Prueba esto:
sed -i ''s/subdomainA/subdomainB/g'' `grep -ril ''subdomainA'' *`
Puedes usar awk para resolver esto como abajo,
for file in `find /home/www -type f`
do
awk ''{gsub(/subdomainA.example.com/,"subdomainB.example.com"); print $0;}'' $file > ./tempFile && mv ./tempFile $file;
done
Espero que esto te ayudará !!!
Según this entrada del blog:
find . -type f | xargs perl -pi -e ''s/oldtext/newtext/g;''
Si desea usar esto sin destruir completamente su repositorio SVN, puede decirle a ''buscar'' que ignore todos los archivos ocultos haciendo:
find . /( ! -regex ''.*//..*'' /) -type f -print0 | xargs -0 sed -i ''s/subdomainA.example.com/subdomainB.example.com/g''
Si no le importa usar vim
junto con grep
o find
herramientas, puede seguir la respuesta dada por el usuario Gert en este enlace -> ¿Cómo hacer un reemplazo de texto en una gran jerarquía de carpetas? .
Aquí está el trato:
grep recursivamente para la cadena que desea reemplazar en una ruta determinada, y tome solo la ruta completa del archivo correspondiente. (ese sería el
$(grep ''string'' ''pathname'' -Rl)
.(opcional) si desea hacer una copia de seguridad de esos archivos en el directorio centralizado, puede usar esto también:
cp -iv $(grep ''string'' ''pathname'' -Rl) ''centralized-directory-pathname''
después de eso, puede editar / reemplazar a voluntad en
vim
siguiendo un esquema similar al proporcionado en el enlace dado:-
:bufdo %s#string#replacement#gc | update
-
Solo necesitaba esto y no estaba contento con la velocidad de los ejemplos disponibles. Así que se me ocurrió lo mío:
cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 perl -i.bak -pe ''s/subdomainA/.example/.com/subdomainB.example.com/g''
Ack-grep es muy eficiente en la búsqueda de archivos relevantes. Este comando reemplazó ~ 145 000 archivos con una brisa mientras que otros tardaron tanto que no podía esperar hasta que terminen.
Solo para evitar cambiar también.
- Casi subdominio.ejemplo.com
- subdominioAejemplo.comp.otro
pero aún
- subdominioA.ejemplo.com.IsIt.good
(Tal vez no sea bueno en la idea detrás de la raíz del dominio)
find /home/www/ -type f -exec sed -i ''s//bsubdomainA/.example/.com/b//1subdomainB.example.com/2/g'' {} /;
Todos los trucos son casi iguales, pero me gusta este:
find <mydir> -type f -exec sed -i ''s/<string1>/<string2>/g'' {} +
find <mydir>
: busca en el directorio.-type f
:El archivo es de tipo: archivo regular
-exec command {} +
:Esta variante de la acción -exec ejecuta el comando especificado en los archivos seleccionados, pero la línea de comandos se construye agregando cada nombre de archivo seleccionado al final; el número total de invocaciones del comando será mucho menor que el número de archivos coincidentes. La línea de comandos está integrada de la misma manera que xargs construye sus líneas de comandos. Solo se permite una instancia de `{} ''dentro del comando. El comando se ejecuta en el directorio de inicio.
Un oneliner agradable como extra. Utilizando git grep.
git grep -lz ''subdomainA.example.com'' | xargs -0 perl -i'''' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
Una forma más sencilla es usar lo siguiente en la línea de comando
find /home/www/ -type f|xargs perl -pi -e ''s/subdomainA/.example/.com/subdomainB.example.com/g''
Usando combinación de grep
y sed
for pp in $(grep -Rl looking_for_string)
do
sed -i ''s/looking_for_string/something_other/g'' "${pp}"
done
Yo solo uso tops
find . -name ''*.[c|cc|cp|cpp|m|mm|h]'' -print0 | xargs -0 tops -verbose replace "verify_noerr(<b args>)" with "__Verify_noErr(<args>)" /
replace "check(<b args>)" with "__Check(<args>)"
para cambiar varios archivos (y guardar una copia de seguridad como *.bak
):
perl -p -i -e "s//|/x/g" *
tomará todos los archivos en el directorio y reemplazará |
con x llamado "pastel Perl" (fácil como un pastel)
grep -lr ''subdomainA.example.com'' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done
Supongo que la mayoría de las personas no saben que pueden canalizar algo en un "archivo mientras se lee" y evita esos desagradables argumentos -print0, al tiempo que ocupa espacios en los nombres de archivo.
Además, agregar un echo
antes de que sed le permite ver qué archivos cambiarán antes de hacerlo.
Nota : no ejecute este comando en una carpeta que incluya un repositorio de git. Los cambios en .git podrían dañar su índice de git.
find /home/www -type f -print0 | xargs -0 sed -i ''s/subdomainA/.example/.com/subdomainB.example.com/g''
Del man find
:
-print0 (solo buscar GNU) le dice a encontrar que use el carácter nulo (/ 0) en lugar del espacio en blanco como el delimitador de salida entre las rutas de acceso encontradas. Esta es una opción más segura si sus archivos pueden contener espacios en blanco u otros caracteres especiales. Se recomienda usar el argumento -print0 para encontrar si usa el comando -exec o xargs (el argumento -0 es necesario en xargs).
Nota : no ejecute este comando en una carpeta que incluya un repositorio de git. Los cambios en .git podrían dañar su índice de git.
find /home/www/ -type f -exec /
sed -i ''s/subdomainA/.example/.com/subdomainB.example.com/g'' {} +
Comparado con otras respuestas aquí, esto es más simple que la mayoría y usa sed en lugar de perl, que es lo que se preguntó en la pregunta original.
Un poco de la vieja escuela pero esto funcionó en OS X.
Hay algunos engaños:
• Solo editará archivos con extensión .sls
bajo el directorio actual
• .
debe escaparse para asegurarse de que sed
no los evalúe como "cualquier personaje"
• ,
se utiliza como delimitador sed
lugar del habitual /
También tenga en cuenta que esto es editar una plantilla Jinja para pasar una variable
en la ruta de una import
(pero esto está fuera de tema).
Primero, verifique que su comando sed haga lo que quiere (esto solo imprimirá los cambios a la salida estándar, no cambiará los archivos):
for file in $(find . -name *.sls -type f); do echo -e "/n$file: "; sed ''s,foo/.bar,foo/bar//"+baz+/"/,g'' $file; done
Edite el comando sed según sea necesario, una vez que esté listo para realizar cambios:
for file in $(find . -name *.sls -type f); do echo -e "/n$file: "; sed -i '''' ''s,foo/.bar,foo/bar//"+baz+/"/,g'' $file; done
Tenga en cuenta el -i ''''
en el comando sed , no quería crear una copia de seguridad de los archivos originales (como se explica en las ediciones in situ con sed en OS X o en el comentario de Robert Lujo en esta página).
Felices personas sedantes!
#!/usr/local/bin/bash -x
find * /home/www -type f | while read files
do
sedtest=$(sed -n ''/^/,/$/p'' "${files}" | sed -n ''/subdomainA/p'')
if [ "${sedtest}" ]
then
sed s''/subdomainA/subdomainB/''g "${files}" > "${files}".tmp
mv "${files}".tmp "${files}"
fi
done
cd /home/www && find . -type f -print0 |
xargs -0 perl -i.bak -pe ''s/subdomainA/.example/.com/subdomainB.example.com/g''
find /home/www/ -type f -exec perl -i.bak -pe ''s/subdomainA/.example/.com/subdomainB.example.com/g'' {} +
find /home/www/ -type f
todos los archivos en / home / www / (y sus subdirectorios). El indicador "-exec" le dice a encontrar que ejecute el siguiente comando en cada archivo encontrado.
perl -i.bak -pe ''s/subdomainA/.example/.com/subdomainB.example.com/g'' {} +
es el comando que se ejecuta en los archivos (muchos a la vez). El {}
se reemplaza por nombres de archivo. El +
al final del comando le dice a find
que compile un comando para muchos nombres de archivo.
De acuerdo con la página de find
manual: "La línea de comandos está integrada de la misma manera que xargs construye sus líneas de comandos".
Por lo tanto, es posible lograr su objetivo (y manejar los nombres de archivos que contienen espacios) sin usar xargs -0
, o -print0
.
perl -p -i -e ''s/oldthing/new_thingy/g'' `grep -ril oldthing *`