Her er min kommando (bryde forsætligt):
grep FOO "/Users/gjtorikian/blah" -l | xargs sed -i "" "/FOO/{s/FOO/BAR/g; w /dev/stdout }"
I højden -niveau: grep
til FOO i blah
kataloget; rør kun filnavnet (på grund af -l
) til sed
; sed
udfører en integreret erstatning (-i ""
) og udskriver kun det ændrede udtryk til /dev/stdout
.
Hvis jeg udelader -l
og rør, jeg får det tilbage fra grep
:
/Users/gjtorikian/blah/baz.cs:1:FOO /Users/gjtorikian/blah/bar.js:1:FOO
Hvad jeg vil have er sed
for at udføre den integrerede udskiftning og derefter vise mig filen og det udskiftede udtryk; for eksempel:
/Users/gjtorikian/blah/baz.cs:1:BAR /Users/gjtorikian/blah/bar.js:1:BAR
Er sådan en ting mulig? Hvis det betyder noget, foretrækker jeg kun at beholde det med grep
/ sed
. Skal jeg lave et sekund grep
efter ?
Kommentarer
- Jeg kan ikke ' ikke se en måde at få det aktuelle filnavn i sed, så du har sandsynligvis brug for en anden grep.
- Du behøver heller ikke '
''
, at ' er standard. - I OS X er
''
ikke standard .
Svar
En mulighed ville være at sende output fra grep
til et separat sed
filter.
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/"
Du kan få sed
til at udskrive linjenummeret (med kommandoen =
), når det finder et match og gøre yderligere efterbehandling.
Det ville sandsynligvis være tydeligere at bruge 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
Svar
Som jeg “mn på den indfødte engelsktalende fik jeg sandsynligvis ikke det.
For at “grep” en mappe har du brug for “-r”. Brug af “-l” udskriver kun filnavn, og det stopper grepping efter første forekomst.
# 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
Svar
Jeg synes, du gør problemet for kompliceret ved at prøve at gøre det på én gang. Du kan bare gøre en
grep -H -n /Users/gjtorikian/blah | sed "s/\(^.*?:[0-9]+?:\).*FOO.*/\1BAZ/"
til få listen over filer med linjenumre og erstatninger (dette skal fungere, så længe dine filnavne ikke indeholder kolon, men det er alligevel en dårlig idé i Mac OS …). Bagefter kan du udstede en
sed -i "" "s/FOO/BAR/g" /Users/gjtorikian/blah
Der kræves ingen grep og xarg her (du kan muligvis foretage en “find … | xarg”, hvis du dog har mange filer). Hvis du er bekymret om duplikeringerne kan du placere de to linjer i et script eller en funktion og udføre variable erstatninger der.
Kommentarer
Svar
Udskift FOO rekursivt med BAR i aktuel dir (stille tilstand)
grep -rl "FOO" | xargs sed -i "s/FOO/BAR/"
Svar
Én liner
grep -n FOO * -R|awk -F\: "{ print $1 }"|sort|uniq|while read file; do sed -i "s/FOO/BAR/g" $file; done
sed -i ...
ændrer filetiderne, selvom strømmen er uændret.-i
overtræder unix-filosofi ( mywiki.wooledge .org / BashFAQ / 021 # Brug_ ikke-standardværktøjer ). Hvis du vil undgå det, skal du brugeex
(f.eks.ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah
).