web-dev-qa-db-ja.com

sedへのgrepパイプ、インラインの置き換え;しかし、私はsedにファイル名と変更された行を印刷させたいです。出来ますか?

ここに私のコマンドがあります(意図的な中断):

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

高レベル:grepディレクトリーのFOOの場合はblah。 (-lのため)ファイル名のみをsedにパイプします。 sedはインライン置換(-i '')を実行し、変更された用語のみ/dev/stdoutに出力します。

-lとパイプを省略した場合、grepからこれを取得します。

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

私が欲しいのは、インライン置換を実行するsedで、置換されたファイルと用語を表示します。例えば:

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

そのようなことは可能ですか?それが重要な場合は、grep/sedのみを使用することをお勧めします。 grepの後に2回目のsedを実行する必要がありますか?

7
gjtorikian

1つの可能性は、grepの出力を別の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/'

sedに行番号を出力させることができます(=コマンド)一致が見つかった場合、さらに後処理を行います。

おそらく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

私はネイティブスピーカーではないので、おそらく理解できませんでした。

ディレクトリを「grep」するには、「-r」が必要です。 '-l'を使用すると、ファイル名だけが出力され、最初に出現した後にgreppingが停止します。

# 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
2
jirib

現在のディレクトリでFOOをBARに再帰的に置換します(静止モード)

grep -rl 'FOO' | xargs sed -i 's/FOO/BAR/'
1
Terentev Maksim