Aquí está mi comando (romper intencionalmente):

grep FOO "/Users/gjtorikian/blah" -l | xargs sed -i "" "/FOO/{s/FOO/BAR/g; w /dev/stdout }" 

En el alto -level: grep para FOO en el directorio blah; canalizar solo el nombre del archivo (debido a -l) a sed; sed realiza un reemplazo en línea (-i "") e imprime solo el término cambiado a /dev/stdout.

Si tuviera que omitir el -l y pipe, obtengo esto de grep:

/Users/gjtorikian/blah/baz.cs:1:FOO /Users/gjtorikian/blah/bar.js:1:FOO 

Lo que quiero es sed para realizar el reemplazo en línea y luego mostrarme el archivo y el término reemplazados; por ejemplo:

/Users/gjtorikian/blah/baz.cs:1:BAR /Users/gjtorikian/blah/bar.js:1:BAR 

¿Es posible tal cosa? Si importa, preferiría mantenerlo con solo grep / sed. ¿Tengo que hacer un segundo grep después de ?

Comentarios

  • No ' no veo una forma de obtener el nombre del archivo actual en sed, por lo que probablemente necesitará un segundo grep.
  • Además, no ' no necesita el '', que ' es el predeterminado.
  • En OS X, '' no es el predeterminado. .

Respuesta

Una posibilidad sería pasar la salida de grep a un sed filtro separado.

 grep -l FOO "/Users/gjtorikian/blah/"* | { tee /dev/fd/3 | xargs sed -i -e "/FOO/{" -e "s/FOO/BAR/g" -e "w /dev/stdout" } 3>&1 | sed "s/:FOO$/:BAR/"  

Puede hacer que sed imprima el número de línea (con el comando =) cuando encuentre una coincidencia y haga más posprocesamiento.

Probablemente sería más claro usar awk.

 for x in "/Users/gjtorikian/blah/"*; do awk " sub(/FOO/, "BAR") {found=1; print FILENAME ":" NR ":" "BAR" >"/dev/stderr"} 1 {print} END {exit(!found)} " "$x" >"$x.tmp" && mv "$x.tmp" "$x" done  

Responder

Como yo «mn Probablemente no lo entendí.

Para «grep» un directorio, necesita «-r». El uso de «-l» imprime solo el nombre del archivo y deja de grepping después de la primera ocurrencia.

# pattern=/home ; grep -l "$pattern" /etc/[a-z]* 2>/dev/null | while read line ; do echo "$line:$pattern" ; done /etc/adduser.conf:/home /etc/fstab:/home /etc/fstab~:/home /etc/libuser.conf:/home /etc/mpd.conf:/home /etc/mpd.conf~:/home /etc/mtab:/home /etc/netscsid.conf:/home /etc/passwd:/home /etc/passwd-:/home /etc/sudoers:/home /etc/sudoers.tmp~:/home 

Respuesta

Creo que está complicando demasiado el problema al intentar hacerlo de una vez. Simplemente puede hacer un

grep -H -n /Users/gjtorikian/blah | sed "s/\(^.*?:[0-9]+?:\).*FOO.*/\1BAZ/" 

para obtenga la lista de archivos con números de línea y reemplazos (esto debería funcionar, siempre y cuando sus nombres de archivo no contengan dos puntos, pero esa es una mala idea en Mac OS de todos modos …).

sed -i "" "s/FOO/BAR/g" /Users/gjtorikian/blah 

Aquí no se necesitan grep ni xarg (puede hacer un «buscar … | xarg» si tiene muchos archivos). Si «está preocupado sobre las duplicaciones, puede poner las dos líneas en un script o función y realizar sustituciones de variables allí.

Comentarios

  • sed -i ... cambia la duración del archivo incluso si la transmisión no ha cambiado.
  • @jww: tienes ' tienes razón, que ' s porque es un editor de flujo, no un editor de archivos, por lo que la opción -i viola la filosofía de Unix ( mywiki.wooledge .org / BashFAQ / 021 # Using_nonstandard_tools ). Si desea evitar eso, use ex (por ejemplo, ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah).

Respuesta

Reemplaza recursivamente FOO con BAR en el directorio actual (modo silencioso)

grep -rl "FOO" | xargs sed -i "s/FOO/BAR/" 

Respuesta

Una línea

grep -n FOO * -R|awk -F\: "{ print $1 }"|sort|uniq|while read file; do sed -i "s/FOO/BAR/g" $file; done 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *