Usando sed y grep / egrep para buscar y reemplazar

97

Estoy usando egrep -Rseguida de una expresión regular que contiene alrededor de 10 uniones, así como: .jpg | .png | .gifetc. Esto funciona bien, ahora me gustaría reemplazar todas las cadenas encontradas con.bmp

Estaba pensando en algo como

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

así que el problema aquí es cómo hago una expresión regular de unión similar sedy cómo le digo que guarde los cambios en el archivo del que obtuvo la entrada.

O yo
fuente
2
Encontré esta pregunta mientras buscaba formas de buscar y reemplazar en varios archivos en una jerarquía de directorios. Para otros en mi situación, intente rpl .
titaniumdecoy
gracias, rpl funciona y es muy fácil de recordar ... solo rpl old_string new_string target_files.
cesarpachon

Respuestas:

183

Utilice este comando:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: encuentra líneas coincidentes usando expresiones regulares extendidas

    • -l: solo lista los nombres de archivo coincidentes

    • -R: busca recursivamente en todos los directorios dados

    • -Z: usar \0como separador de registros

    • "\.jpg|\.png|\.gif": coincide con una de las cadenas ".jpg", ".gif"o".png"

    • .: inicia la búsqueda en el directorio actual

  • xargs: ejecuta un comando con stdin como argumento

    • -0: utilizar \0como separador de registros. Esto es importante para hacer coincidir el -Zde egrepy para evitar ser engañado por espacios y líneas nuevas en los nombres de archivo de entrada.

    • -l: use una línea por comando como parámetro

  • sed: La s tream ed Itor

    • -i: reemplace el archivo de entrada con la salida sin hacer una copia de seguridad

    • -e: usa el siguiente argumento como expresión

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': reemplaza todas las apariciones de las cadenas ".jpg", ".gif"o ".png"con".bmp"

David Schmitt
fuente
todo funciona excepto el | en la parte sed. No entiendo por qué, ya que tiene sentido ... la parte -l de xargs me estaba dando errores, así que lo eliminé, ¿podría estar relacionado?
Ori
Descubrí que este comando agrega una nueva línea al final de todos los archivos que procesa.
titaniumdecoy
@titanumdecoy: No pude reproducir este comportamiento. ¿Qué versión de sed estabas usando y en qué sistema operativo estás?
David Schmitt
1
@DavidSchmitt: Probablemente desee utilizar sed -rpara expresiones regulares extendidas. En ese punto, el patrón coincidirá con lo que se usa en egrep, y es posible que desee ponerlo en una variable para reutilizarlo.
bukzor
este comando me ahorró horas de trabajo copiando archivos de encabezado de mi aplicación para la biblioteca que hice. Esto es increíble :) Aquí está el comando que usé egrep -lRZ "\ .h $". | xargs -0 tar -cvf headers.tar | (cp headers.tar headers; cd headers; tar xf headers.tar;)
The Lazy Coder
11

Honestamente, por mucho que me guste sed para las tareas apropiadas, esta es definitivamente una tarea para perl; es realmente más poderosa para este tipo de frases ingeniosas, especialmente para "escribirlo de nuevo de donde viene" (el -iinterruptor de perl lo hace por usted, y opcionalmente también le permite mantener la versión anterior, por ejemplo, con un .bak adjunto, solo use -i.baken su lugar).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

en lugar de un trabajo complejo en sed (si es posible allí) o awk ...

Alex Martelli
fuente
6
sed usa -i, al igual que perl.
Stobor
@Stobor: juro que he tenido problemas en los que la operación de perl cuando alimenté la cadena de reemplazo de expresiones regulares hizo exactamente lo que quería, a diferencia de sed, incluso si le di la opción de expresiones regulares a sed... Creo que olvidé algunas banderas para sed o tenía limitaciones.
meder omuraliev
10

Otra forma de hacer esto

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Alguna ayuda con respecto al comando anterior

La búsqueda hará la búsqueda por usted en el directorio actual indicado por .

-name el nombre del archivo en mi caso su pom.xml puede dar comodines.

-exec ejecutar

sed editor de flujo

-i ignorar caso

s es para sustituto

/4.6.0.../ Cadena a buscar

/5.0.0.../ Cadena para ser reemplazada

Cyril Cherian
fuente
tal vez no sea tan poderoso, pero esto es mucho más fácil de entender para mí que la respuesta aceptada
icc97
5

No pude hacer que ninguno de los comandos en esta página funcionara para mí: la solución sed agregó una nueva línea al final de todos los archivos que procesó, y la solución de perl no pudo aceptar suficientes argumentos de find. Encontré esta solución que funciona perfectamente:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

Esto se repetirá hacia abajo en el árbol de directorios actual y se reemplazará search_regexcon replacement_stringcualquier archivo que termine en .ho .m.

También he usado rpl para este propósito en el pasado.

titanio decoy
fuente
0

prueba algo usando un bucle for

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

no es bonito, pero debería hacerlo.

Don Johe
fuente
3
xargs es definitivamente más apropiado aquí.
Nathan Fellman
1
y el uso del |while read ipatrón permitiría la transmisión y evitaría restricciones de duración cuando los resultados de egrep fueran demasiado largos
David Schmitt
0

Mi caso de uso fue que quería reemplazar foo:/Drive_Lettercon foo:/bar/baz/xyz En mi caso, pude hacerlo con el siguiente código. Estaba en la misma ubicación del directorio donde había una gran cantidad de archivos.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

Espero que haya ayudado.

ACTUALIZACIÓN s | foo: / Drive_letter: | foo: / ba / baz / xyz | g

neo7
fuente
Puede usar otros delimitadores para el comando sed, y hacerlo hace que los nombres de ruta sean mucho más agradables:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Kevin