Reemplazar cadenas en archivos basados en ciertos criterios de búsqueda es una tarea muy común. Cómo puedo
- reemplazar cadena
foo
conbar
en todos los archivos en el 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 determinado contexto?
- reemplazar si la cadena está en un cierto número de línea?
- reemplazar múltiples cadenas con el mismo reemplazo
- reemplazar múltiples cadenas con diferentes reemplazos
text-processing
awk
sed
perl
terdon
fuente
fuente
Respuestas:
1. Reemplazar todas las ocurrencias 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 regulares y que desea procesar todos los archivos no ocultos. Si ese no es el caso, use los enfoques en 2.
Todas las
sed
soluciones en esta respuesta asumen GNUsed
. Si usa FreeBSD o OS / X, reemplácelo-i
con-i ''
. También tenga en cuenta que el uso del-i
conmutador con cualquier versión desed
tiene ciertas implicaciones de seguridad del sistema de archivos y no es aconsejable en cualquier script que planee distribuir de ninguna manera.Archivos no recursivos en este directorio solamente:
(el
perl
que fallará para los nombres de archivo que terminan en|
o espacio) ).Archivos regulares y recursivos ( incluidos los ocultos ) en este y todos los subdirectorios
Si está usando zsh:
(puede fallar si la lista es demasiado grande, ver
zargs
para evitar).Bash no puede verificar directamente los archivos normales, se necesita un bucle (los paréntesis evitan configurar las opciones globalmente):
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 en este directorio solamente:
Archivos regulares y recursivos en este y todos los subdirectorios
Si está utilizando bash (las llaves evitan configurar las opciones globalmente):
Si está usando zsh:
El
--
sirve para decirsed
que no se darán más banderas en la línea de comando. Esto es útil para proteger contra los nombres de archivos que comienzan con-
.Si un archivo es de cierto tipo, por ejemplo, ejecutable (ver
man find
más opciones):zsh
:3. Reemplace solo si la cadena se encuentra en un contexto determinado
Reemplace
foo
conbar
solo si hay unbaz
posterior en la misma línea:En
sed
, el uso de\( \)
guardar 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 tales expresiones regulares, consulte aquí .Reemplace
foo
conbar
solo sifoo
se encuentra en la columna 3d (campo) del archivo de entrada (suponiendo campos separados por espacios en blanco):(necesita
gawk
4.1.0 o más reciente).Para un campo diferente, simplemente use
$N
whereN
es el número del campo de interés. Para un separador de campo diferente (:
en este ejemplo) use:Otra solución usando
perl
:NOTA: tanto el
awk
yperl
soluciones afectará espaciado en el archivo (eliminar los espacios en blanco de ataque y salida, y convertir secuencias de espacios en blanco para un carácter de espacio en aquellas líneas que responden). Para un campo diferente, use$F[N-1]
dóndeN
está el número de campo que desea y para un separador de campo diferente ($"=":"
establece el separador de campo de salida en:
):Reemplace
foo
conbar
solo en la cuarta línea:4. Operaciones de reemplazo múltiple: reemplace con diferentes cadenas
Puedes combinar
sed
comandos:Tenga en cuenta que el orden es importante (
sed 's/foo/bar/g; s/bar/baz/g'
sustituiráfoo
conbaz
).o comandos de Perl
Si tiene una gran cantidad de patrones, es más fácil guardar sus patrones y sus reemplazos en un
sed
archivo de script:O, 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):
Eso será bastante lento para largas listas de patrones y archivos de datos grandes, por lo que es posible que desee leer los patrones y crear un
sed
script a partir de ellos. Lo siguiente supone que un delimitador <space> separa una lista de pares MATCH <space> REPLACE que ocurren uno por línea en el archivopatterns.txt
:El formato anterior es en gran medida arbitrario y, por ejemplo, no permite un <space> en MATCH o REPLACE . Sin embargo, el método es muy general: básicamente, si puede crear una secuencia de salida que se parezca a una
sed
secuencia de comandos, puede generar esa secuencia comosed
secuencia de comandos especificandosed
el archivo de secuencia de comandos como-
stdin.Puede combinar y concatenar múltiples scripts de manera similar:
Un POSIX
sed
concatenará todos los scripts en uno en el orden en que aparecen en la línea de comandos. Ninguno de estos debe terminar en una línea\n
electrónica.grep
puede funcionar de la misma manera: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:
5. Operaciones de reemplazo múltiple: reemplaza múltiples patrones con la misma cadena
Reemplace cualquiera de
foo
,bar
obaz
confoobar
o
fuente
zsh
. Por supuesto, agreguezsh
información, pero no hay ninguna razón para eliminar las cosas de bash. Además, sé que usar el shell para el procesamiento de texto no es ideal, pero hay casos en los que es necesario. Edité una versión mejor de mi script original que creará unsed
script en lugar de usar el bucle de shell para analizar. Esto puede ser útil si tiene varios cientos de pares de patrones, por ejemplo.(.)
calificador global, por lo que no se puede usar aquí. (también te estás perdiendo algo). El bucle for es incorrecto (falta -r) y significa realizar varias pasadas en los archivos y no agrega ningún beneficio sobre un script sed.--
despuéssed -i
y antes del comando sustituto?-
. Su uso asegura que los comandos funcionarán en archivos con nombres como-foo
. Sin él, el-f
sería analizado como una opción..git
directorio, y realmente estropearán su pago. Es mejor operar dentro / sobre directorios específicos por nombre.Una buena r e pl acement herramienta de Linux es RPL , que fue escrito originalmente para el proyecto Debian, por lo que está disponible con
apt-get install rpl
en cualquier distro derivada de Debian, y puede ser para los demás, pero por lo demás se puede descargar eltar.gz
archivo en SourgeForge .El ejemplo más simple de uso:
Tenga en cuenta que si la cadena contiene espacios, debe estar entre comillas. De forma predeterminada,
rpl
se ocupan de las letras mayúsculas pero no de las palabras completas , pero puede cambiar estos valores predeterminados con opciones-i
(ignorar mayúsculas y minúsculas) y-w
(palabras completas). También puede especificar múltiples archivos :O incluso especifique las extensiones (
-x
) para buscar o incluso busque recursivamente (-R
) en el directorio:También puede buscar / reemplazar en modo interactivo con
-p
la opción (solicitud):El resultado muestra los números de archivos / cadenas reemplazados y el tipo de búsqueda (mayúsculas / minúsculas, palabras completas / parciales), pero puede ser silencioso con la opción
-q
( modo silencioso ), o incluso más detallado, enumerando números de línea que contienen coincidencias de cada archivo y directorio con la opción-v
( modo detallado ).Otras opciones que vale la pena recordar son
-e
(honor e scapes) que permitenregular expressions
, por lo que puede buscar también pestañas (\t
), nuevas líneas (\n
), etc. Incluso puede usar-f
para forzar permisos (por supuesto, solo cuando el usuario tiene permisos de escritura) y-d
para preservar los tiempos de modificación ').Finalmente, si no está seguro de cuál será exactamente, use el
-s
( modo de simulación ).fuente
Cómo hacer una búsqueda y reemplazar varios archivos sugiere:
Mis mejores resultados provienen del uso de perl y grep (para asegurar que el archivo tenga la expresión de búsqueda)
fuente
Puede usar Vim en modo Ex:
fuente
Usé esto:
Lista todos los archivos que contienen
old_string
.Reemplace la nueva línea en el resultado con espacios (para poder alimentar la lista de archivos
sed
.Ejecutar
sed
en esos archivos para reemplazar la cadena vieja con nueva.Actualización: el resultado anterior fallará en los nombres de archivo que contienen espacios en blanco. En cambio, use:
grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'
fuente
grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'
lo hará tratar con nombres de archivos arbitrarios.Desde la perspectiva del usuario, una herramienta Unix agradable y simple que hace el trabajo perfectamente es
qsubst
. Por ejemplo,reemplazará
foo
conbar
en todos mis archivos C. Una buena característica es queqsubst
hará un reemplazo de consulta , es decir, me mostrará cada apariciónfoo
y me preguntará si quiero reemplazarlo o no. [Puede reemplazar incondicionalmente (sin preguntar) con la-go
opción, y hay otras opciones, por ejemplo,-w
si solo desea reemplazarfoo
cuando es una palabra completa.]Cómo conseguirlo:
qsubst
fue inventado por der Mouse (de McGill) y publicado en comp.unix.sources 11 (7) en agosto de 1987. Existen versiones actualizadas. Por ejemplo, la versión de NetBSD seqsubst.c,v 1.8 2004/11/01
compila y se ejecuta perfectamente en mi Mac.fuente
Necesitaba algo que podría proporcionar una opción de funcionamiento en seco y trabajaría de forma recursiva con un pegote, y después de probar a hacerlo con
awk
ysed
me di por vencido y en su lugar hice en pitón.El script busca recursivamente todos los archivos que coinciden con un patrón global (por ejemplo
--glob="*.html"
) para una expresión regular y la reemplaza con la expresión regular de reemplazo:Cada opción larga como
--search-regex
tiene una opción corta correspondiente, es decir-s
. Ejecute con-h
para ver todas las opciones.Por ejemplo, esto cambiará todas las fechas de
2017-12-31
a31-12-2017
:fuente
globstar
opción de bash (o el equivalente de su shell) y**
globs ofind
. Para una carrera en seco, solo usesed
. A menos que use la-i
opción, no hará ningún cambio. Para uso de respaldosed -i.bak
(operl -i .bak
); para archivos que no coinciden, usegrep PATTERN file || echo file
. ¿Y por qué en el mundo tendrías a Python expandir el globo en lugar de dejar que el shell lo haga? ¿Por qué enscript.py --glob=foo*
lugar de soloscript.py foo*
?sed
yawk
bien y no estar dispuesto a invertir tiempo extra en el dominio de ellos, (4) la legibilidad, (5) esta solución también trabajará en sistemas no POSIX (No es que lo necesite, pero alguien más podría hacerlo).ripgrep (nombre del comando
rg
) es unagrep
herramienta, pero también admite la búsqueda y el reemplazo.rg
no admite la opción in situ, por lo que deberá hacerlo usted mismoConsulte la documentación de Rust regex para conocer la sintaxis y las características de las expresiones regulares. El
-P
interruptor habilitará el sabor PCRE2 .rg
admite Unicode de forma predeterminada.Al igual que
grep
la-F
opción, permitirá que las cadenas fijas coincidan, una opción práctica que creo que tambiénsed
debería implementarse.Otra opción práctica es la
-U
que permite la coincidencia de líneas múltiplesrg
también puede manejar archivos de estilo dosOtra ventaja de
rg
es que es probable que sea más rápido quesed
fuente