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

  • sed -i ... ændrer filetiderne, selvom strømmen er uændret.
  • @jww: du ' har ret, at ' s fordi er en streameditor, ikke en fileditor, så indstillingen -i overtræder unix-filosofi ( mywiki.wooledge .org / BashFAQ / 021 # Brug_ ikke-standardværktøjer ). Hvis du vil undgå det, skal du bruge ex (f.eks. ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah).

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 

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *