sustituir - sed linux
Cómo reemplazar una cadena en varios archivos en la línea de comandos de Linux (22)
"También podrías usar find y sed, pero encuentro que esta pequeña línea de perl funciona muy bien.
perl -pi -w -e ''s/search/replace/g;'' *.php
- -e significa ejecutar la siguiente línea de código.
- -i significa editar en el lugar
- -w escribir advertencias
- -p bucle
"(Extraído de http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.php )
Mis mejores resultados provienen del uso de perl y grep (para garantizar que el archivo tenga la expresión de búsqueda)
perl -pi -w -e ''s/search/replace/g;'' $( grep -rl ''search'' )
Necesito reemplazar una cadena en muchos archivos en una carpeta, con solo el acceso ssh
al servidor. ¿Cómo puedo hacer esto?
$ replace "From" "To" - `find / path / to / folder -type f`
(reemplazar: una utilidad de reemplazo de cadenas del sistema de base de datos MySQL)
El editor de secuencias modifica múltiples archivos "in situ" cuando se invoca con el modificador -i
, que toma un archivo de copia de seguridad que termina como argumento. Asi que
sed -i.bak ''s/foo/bar/g'' *
reemplaza foo
con bar
en todos los archivos de esta carpeta, pero no desciende a subcarpetas. Sin embargo, esto generará un nuevo archivo .bak
para cada archivo en su directorio. Para hacer esto recursivamente para todos los archivos en este directorio y todos sus subdirectorios, necesita un ayudante, como find
, para atravesar el árbol de directorios.
find ./ -print0 | xargs -0 sed -i.bak ''s/foo/bar/g'' *
find
permite restricciones adicionales sobre qué archivos modificar, especificando argumentos adicionales como find ./ -name ''*.php'' -or -name ''*.html'' -print0
, si es necesario.
Nota: GNU sed
no requiere la finalización de un archivo, sed -i ''s/foo/bar/g'' *
también funcionará; FreeBSD sed
exige una extensión, pero permite un espacio intermedio, por lo que sed -i .bak s/foo/bar/g *
funciona.
El siguiente comando se puede usar para buscar primero los archivos y reemplazar los archivos
encontrar . | xargs grep cadena de búsqueda | sed ''s / string string / new string / g''
por ejemplo, encontrar | xargs grep abc | sed ''s / abc / xyz / g''
En caso de que su cadena tenga una barra (/) hacia delante, podría cambiar el delimitador a ''+''.
find . -type f -exec sed -i ''s+http://example.com+https://example.com+g'' {} +
Este comando se ejecutaría recursivamente en el directorio actual.
Encontré este en otro post (no recuerdo cuál) y aunque no es el más elegante, es simple y como usuario novato de Linux no me ha dado ningún problema.
for i in *old_str* ; do mv -v "$i" "${i//old_str/new_str}" ; done
Si tienes espacios u otros caracteres especiales usa un /
for i in *old_str/ * ; do mv -v "$i" "${i//old_str/ /new_str}" ; done
para cadenas en subdirectorios **
for i in */*old_str/ * ; do mv -v "$i" "${i//old_str/ /new_str}" ; done
Esto funcionó para mí:
find ./ -type f -exec sed -i ''s/string1/string2/'' {} /;
Sin embargo, esto no fue así: sed -i ''s/string1/string2/g'' *
. Tal vez "foo" no estaba destinado a ser string1 y "barra" no string2.
Estoy dando un ejemplo para corregir un error común de shebang en las fuentes de python.
Puedes probar el enfoque grep / sed. Aquí hay uno que funciona con GNU sed y no romperá un repositorio de git:
$ grep -rli --exclude ''*.git*'' ''#!/usr/bin/python'' . | xargs -I {} /
gsed -i '''' -e ''s/#!//usr//bin//python/#!//usr//bin//env python/'' {}
O puedes usar greptile :)
$ greptile -x .py -l -i -g ''#!/usr/bin/env python'' -r ''#!/usr/bin/python'' .
Acabo de probar el primer script, y el segundo debería funcionar también. Tenga cuidado con los personajes de escape, creo que debería ser más fácil de usar greptile en la mayoría de los casos. Por supuesto, puedes hacer muchas cosas interesantes con sed, y para eso puede ser preferible dominarlo con xargs.
Hay algunas respuestas estándar a esto ya enumeradas. Por lo general, puede usar la función Buscar para listar recursivamente los archivos y luego realizar las operaciones con sed o perl .
Para la mayoría de los usos rápidos, puede encontrar que el comando rpl es mucho más fácil de recordar. Aquí está el reemplazo (foo -> barra), recursivamente en todos los archivos:
rpl -R foo bar .
Probablemente necesites instalarlo ( apt-get install rpl
o similar).
Sin embargo, para los trabajos más difíciles que involucran expresiones regulares y sustitución de respaldo, o cambio de nombre de archivo, así como búsqueda y reemplazo, la herramienta más general y poderosa que conozco es la repren , un pequeño script de Python que escribí hace un tiempo para algunos Tareas de cambio de nombre y refactorización más espinosas. Las razones por las que podrías preferirlo son:
- Admite el cambio de nombre de los archivos, así como la búsqueda y reemplazo de los contenidos de los archivos.
- Vea los cambios antes de comprometerse a realizar la búsqueda y reemplazar.
- Admite expresiones regulares con sustitución de espaldas, palabras completas, mayúsculas y minúsculas y modos de preservación de mayúsculas (reemplaza foo -> bar, Foo -> Bar, FOO -> BAR).
- Funciona con múltiples reemplazos, incluidos los swaps (foo -> barra y barra -> foo) o conjuntos de reemplazos no únicos (foo -> barra, f -> x).
Para utilizarlo, pip install repren
. repren para ver ejemplos
Inventé mi propia solución antes de encontrar esta pregunta (y respuestas). Busqué diferentes combinaciones de "reemplazar" "varias" y "xml", porque esa era mi aplicación, pero no encontré esta en particular.
Mi problema: tenía archivos spring xml con datos para casos de prueba, que contenían objetos complejos. Un refactor en el código fuente de Java cambió muchas clases y no se aplicó a los archivos de datos xml. Para guardar los datos de casos de prueba, tuve que cambiar todos los nombres de clase en todos los archivos xml, distribuidos en varios directorios. Todo mientras se guardan copias de respaldo de los archivos XML originales (aunque esto no era obligatorio, ya que el control de versiones me salvaría aquí).
Estaba buscando una combinación de find
+ sed
, porque funcionó para mí en otras situaciones, pero no con varios reemplazos a la vez.
Luego encontré la respuesta de preguntar ubuntu y me ayudó a construir mi línea de comandos:
find -name "*.xml" -exec sed -s --in-place=.bak -e ''s/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g'' {} /;
Y funcionó perfectamente (bueno, mi caso tenía seis reemplazos diferentes). Pero tenga en cuenta que tocará todos los archivos * .xml en el directorio actual. Debido a eso, y si usted es responsable ante un sistema de control de versiones, es posible que desee filtrar primero y solo pasar a aquellos que realmente tienen las cadenas que desea; me gusta:
find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} /; -exec sed -s --in-place=.bak -e ''s/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g'' {} /;
La respuesta de @kev es buena, pero solo afecta a los archivos en el directorio inmediato. El siguiente ejemplo utiliza grep para buscar archivos de forma recursiva. Funciona para mí cada vez.
grep -rli ''old-word'' * | xargs -i@ sed -i ''s/old-word/new-word/g'' @
Las primeras apariciones de la línea de "foo" serán reemplazadas por "barra". Y puedes usar la segunda línea para comprobar.
grep -rl ''foo'' . | xargs sed -i ''s/foo/bar/g''
grep ''foo'' -r * | awk -F: {''print $1''} | sort -n | uniq -c
Para mantener mi nodo personal en inglés, escribí un script de utilidad que ayudaba a reemplazar varios pares de cadenas antiguas / nuevas, para todos los archivos en un directorio recursivamente.
El par múltiple de la cadena antigua / nueva se administra en un mapa hash.
El directorio se puede establecer a través de la línea de comando o la variable de entorno, el mapa está codificado en el script, pero puede modificar el código para cargar desde un archivo, si es necesario.
Requiere bash 4.2, debido a alguna característica nueva.
en_standardize.sh:
#! /bin/bash
# (need bash 4.2+,)
#
# Standardize phonetic symbol of English.
#
# format:
# en_standardize.sh [<dir>]
#
# params:
# * dir
# target dir, optional,
# if not specified then use environment variable "$node_dir_en",
# if both not provided, then will not execute,
# *
#
paramCount=$#
# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
echo -e "dir specified (in command):/n/t$1/n"
targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
echo -e "dir specified (in environment vairable):/n/t$node_dir_en/n"
targetDir=$node_dir_en
else # environable not set,
echo "dir not specified, won''t execute"
exit
fi
# check whether dir exists,
if [ -d $targetDir ]; then
cd $targetDir
else
echo -e "invalid dir location:/n/t$targetDir/n"
exit
fi
# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")
# print item maps,
echo ''maps:''
for key in "${!itemMap[@]}"; do
echo -e "/t$key/t->/t${itemMap[$key]}"
done
echo -e ''/n''
# do replace,
for key in "${!itemMap[@]}"; do
grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done
echo -e "/nDone."
exit
Para reemplazar una ruta dentro de los archivos (evitando los caracteres de escape) puede usar el siguiente comando:
sed -i ''s@old_path@new_path@g''
El signo @ significa que todos los caracteres especiales deben ignorarse en la siguiente cadena.
Realmente cojo, pero no pude hacer que ninguno de los comandos sed funcionara correctamente en OSX, así que hice esta tontería en su lugar:
:%s/foo/bar/g
:wn
^: copie estas tres líneas en mi portapapeles (sí, incluya la nueva línea final), luego:
vi *
y mantenga presionado command-v hasta que diga que no queda ningún archivo.
Mudo ... Hacky ... eficaz ...
Si el archivo contiene barras diagonales inversas (por lo general, las rutas), puede intentar algo como esto:
sed -i -- ''s,<path1>,<path2>,g'' *
ex:
sed -i -- ''s,/foo/bar,/new/foo/bar,g'' *.sh (in all shell scripts available)
Si tienes lista de archivos puedes usar
replace "old_string" "new_string" -- file_name1 file_name2 file_name3
Si tienes todos los archivos que puedes usar
replace "old_string" "new_string" -- *
Si tiene una lista de archivos con extensión, puede usar
replace "old_string" "new_string" -- *.extension
Similar a la respuesta de Kaspar pero con la bandera g para reemplazar todas las ocurrencias en una línea.
find ./ -type f -exec sed -i ''s/string1/string2/g'' {} /;
Para el caso global insensible:
find ./ -type f -exec sed -i ''s/string1/string2/gI'' {} /;
Para reemplazar una cadena en varios archivos puede usar:
grep -rl string1 somedir/ | xargs sed -i ''s/string1/string2/g''
P.ej
grep -rl ''windows'' ./ | xargs sed -i ''s/windows/linux/g''
script para comando multiedit
multiedit [-n PATTERN] OLDSTRING NEWSTRING
A partir de la respuesta de Kaspar, hice una secuencia de comandos bash para aceptar argumentos de la línea de comandos y, opcionalmente, limitar los nombres de archivos que coinciden con un patrón. Guarde en su $ PATH y haga un ejecutable, luego solo use el comando de arriba.
Aquí está el guión:
#!/bin/bash
_help="/n
Replace OLDSTRING with NEWSTRING recursively starting from current directory/n
multiedit [-n PATTERN] OLDSTRING NEWSTRING/n
[-n PATTERN] option limits to filenames matching PATTERN/n
Note: backslash escape special characters/n
Note: enclose STRINGS with spaces in double quotes/n
Example to limit the edit to python files:/n
multiedit -n /*.py /"OLD STRING/" NEWSTRING/n"
# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then # if -n option is given:
# replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} /;
else
# replace OLDSTRING with NEWSTRING recursively in all files
find ./ -type f -exec sed -i "s/$1/$2/" {} /;
fi
cd /path/to/your/folder
sed -i ''s/foo/bar/g'' *
Las ocurrencias de "foo" serán reemplazadas por "bar".
grep --include={*.php,*.html} -rnl ''./'' -e "old" | xargs -i@ sed -i ''s/old/new/g'' @