Aici este comanda mea (pauză intenționată):
grep FOO "/Users/gjtorikian/blah" -l | xargs sed -i "" "/FOO/{s/FOO/BAR/g; w /dev/stdout }"
La maxim -nivel: grep
pentru FOO în directorul blah
; introduceți doar numele fișierului (din cauza -l
) la sed
; sed
efectuează o înlocuire în linie (-i ""
) și imprimă doar termenul modificat în /dev/stdout
.
Dacă ar omite -l
și pipe, obțin acest lucru înapoi de la grep
:
/Users/gjtorikian/blah/baz.cs:1:FOO /Users/gjtorikian/blah/bar.js:1:FOO
Ce vreau este sed
pentru a efectua înlocuirea în linie și apoi arată-mi fișierul și termenul înlocuit; de exemplu:
/Users/gjtorikian/blah/baz.cs:1:BAR /Users/gjtorikian/blah/bar.js:1:BAR
Este posibil așa ceva? Dacă contează, aș prefera să-l păstrez numai cu grep
/ sed
. Trebuie să fac un al doilea grep
după ?
Comentarii
- Nu ' nu văd o modalitate de a obțineți numele fișierului curent în sed, deci probabil veți avea nevoie de un al doilea grep.
- De asemenea, nu aveți nevoie de ' de
''
, că ' este valoarea implicită. - Pe OS X,
''
nu este valoarea implicită .
Răspuns
O posibilitate ar fi să trimiți rezultatul grep
la un filtru separat sed
.
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/"
Puteți face sed
să imprimați numărul liniei (cu comanda =
) atunci când găsește o potrivire și faceți mai departe postprocesare.
Probabil ar fi mai clar să folosiți 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
Răspuns
As I „mn Vorbitor nativ de engleză, probabil nu l-am obținut.
Pentru a „grep” un director aveți nevoie de „-r”. Utilizarea „-l” tipărește doar numele fișierului și se oprește grepping-ul după prima apariție.
# 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
Răspuns
Cred că faceți problema prea complicată încercând să o faceți dintr-o singură dată. Pur și simplu puteți face o
grep -H -n /Users/gjtorikian/blah | sed "s/\(^.*?:[0-9]+?:\).*FOO.*/\1BAZ/"
obțineți lista de fișiere cu numere de linie și înlocuiri (acest lucru ar trebui să funcționeze, atâta timp cât numele fișierelor dvs. nu conțin două puncte, dar „oricum este o idee proastă în Mac OS …). După aceea, puteți emite un
sed -i "" "s/FOO/BAR/g" /Users/gjtorikian/blah
Nu este nevoie de grep și xarg aici (puteți face un „find … | xarg” dacă aveți totuși o mulțime de fișiere). Dacă sunteți îngrijorat despre duplicări, puteți pune cele două linii într-un script sau funcție și puteți face substituiri variabile acolo.
Comentarii
Răspunde
Înlocuiește recursiv FOO cu BAR în dir curent (mod silențios)
grep -rl "FOO" | xargs sed -i "s/FOO/BAR/"
Răspuns
O linie
grep -n FOO * -R|awk -F\: "{ print $1 }"|sort|uniq|while read file; do sed -i "s/FOO/BAR/g" $file; done
sed -i ...
modifică durata fișierelor chiar dacă fluxul este neschimbat.-i
încalcă filosofia unix ( mywiki.wooledge .org / BashFAQ / 021 # Using_nonstandard_tools ). Dacă doriți să evitați această utilizare, utilizațiex
(de ex.,ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah
).