Her er min kommando (bryt forsettlig):

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

På høy -nivå: grep for FOO i katalogen blah; rør bare filnavnet (på grunn av -l) til sed; sed utfører en innebygd erstatning (-i "") og skriver ut bare det endrede uttrykket til /dev/stdout.

Hvis jeg skulle utelate -l og rør, jeg får dette tilbake fra grep:

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

Det jeg vil ha er sed for å utføre den innebygde erstatningen, og deretter vise meg filen og begrepet erstattet. For eksempel:

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

Er en slik ting mulig? Hvis det betyr noe, foretrekker jeg å beholde det bare med grep / sed. Må jeg gjøre et sekund grep etter ?

Kommentarer

  • Jeg ser ikke ' t å se en måte å få det nåværende filnavnet i sed, så du vil sannsynligvis trenge en ny grep.
  • Du trenger heller ikke ' '', at ' er standard.
  • I OS X er ikke '' standard .

Svar

En mulighet vil være å sende utdataene til grep til et eget 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 å skrive ut linjenummeret (med = -kommandoen) når den finner samsvar og gjøre videre etterbehandling.

Det ville sannsynligvis være tydeligere å bruke 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å engelsk som morsmål fikk jeg sannsynligvis ikke det.

For å «grep» en katalog du trenger «-r». Bruk av «-l» skriver ut bare filnavn, og det stopper grepping etter 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 tror du gjør problemet for komplisert ved å prøve å gjøre det på en gang. Du kan ganske enkelt gjø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å lenge filnavnene dine ikke inneholder kolon, men det er uansett en dårlig ide i Mac OS …). Etterpå kan du utstede en

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

Ingen grep og xarg er nødvendig her (du kan gjøre et «finn … | xarg» hvis du har mange filer skjønt). Hvis du er bekymret om duplikasjonene, kan du sette de to linjene i et skript eller en funksjon og gjøre variable erstatninger der.

Kommentarer

  • sed -i ... endrer filetiden selv om strømmen er uendret.
  • @jww: du ' har rett, at ' s fordi er en streameditor og ikke en filredigerer, så -i -alternativet bryter med unix-filosofien ( mywiki.wooledge .org / BashFAQ / 021 # Using_nonstandard_tools ). Hvis du vil unngå det, bruk ex (f.eks. ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah).

Svar

Erstatt FOO rekursivt med BAR i gjeldende dir (stille modus)

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

Svar

En liner

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

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *