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
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
sed -i ...
cambia la duración del archivo incluso si la transmisión no ha cambiado.-i
viola la filosofía de Unix ( mywiki.wooledge .org / BashFAQ / 021 # Using_nonstandard_tools ). Si desea evitar eso, useex
(por ejemplo,ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah
).