web-dev-qa-db-ja.com

「sed」インプレース変更を報告する方法

sedを使用して文字列をインプレースで置換する場合、それが行う変更を(古いファイルと新しいファイルの差分に依存せずに)報告する方法はありますか?

たとえば、コマンドラインを変更するにはどうすればよいですか

find . -type f | xargs sed -i 's/abc/def/g'

オンザフライで行われた変更を確認できますか?

26
ricab

システムでサポートされている場合は、sedwフラグを/dev/stderr/dev/tty/dev/fd/2と一緒に使用できます。例えば。次のような入力file

foo first
second: missing
third: foo
none here

ランニング

sed -i '/foo/{
s//bar/g
w /dev/stdout
}' file

出力:

bar first
third: bar

fileの内容は次のように変更されました:

bar first
second: missing
third: bar
none here

したがって、あなたの場合、実行します:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
s//bar/g
w /dev/fd/2
}' {} \;

ファイルをその場で編集して出力します。

 
 ./ file1:
 bar stuff 
 more bar 
 
 ./ file2:
 
 ./ file3:
最初にバー
 3番目:バー

original line >>> modified lineのようなものを印刷することもできます。例:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
h
s//bar/g
H
x
s/\n/ >>> /
w /dev/fd/2
x
}' {} \;

ファイルをその場で編集して出力します。

 
 ./ file1:
 foo stuff >>> bar stuff 
 more foo >>> more bar 
 
 ./ file2:
 
 ./ file3:
 foo first >>> bar first 
 third:foo >>> third:bar 
18
don_crissti

最初のパスでprintアクションを使用して、2つのパスでそれを行うことができます。

find . -type f | xargs sed --quiet 's/abc/def/gp'

どこ --quietを指定すると、sedはすべての行を表示せず、p接尾辞は置換が一致した行のみを表示します。

これには、sedが表示しないという制限がありますwhichファイルは変更されますが、もちろん、さらに複雑に修正することができます。

18
msw

それは可能ではないと思いますが、回避策として、代わりにPerlを使用することが考えられます。

find . -type f | xargs Perl -i -ne 's/abc/def/ && print STDERR' 

altered行を標準エラーに出力します。例えば:

$ cat foo
fooabcbar
$ find . -type f | xargs Perl -i -ne 's/abc/def/ && print STDERR' 
foodefbar

これを少し複雑にして、行番号、ファイル名、元の行、変更された行を出力することもできます。

$ find . -type f | 
   xargs Perl -i -ne '$was=$_; chomp($was);
                      s/abc/def/ && print STDERR "$ARGV($.): $was : $_"' 
./foo(1): fooabcbar : foodefbar
8
terdon

現在のパターンをファイルに書き込むwフラグを使用できます。したがって、それを代替コマンドに追加することにより、連続した代替をファイルに報告し、ジョブの完了後にそれを印刷できます。また、置換後の文字列をgrepで色分けしたい。

sed -i -e "s/From/To/gw /tmp/sed.done" file_name
grep --color -e "To" /tmp/sed.done

wとそのファイル名の間にはスペースが1つだけ必要であることに注意してください。

Sedで作成されていない場合でもdiffで変更が表示される可能性があるため、これはdiffよりも優れています。

4
Jack

@terdonソリューションが好きです-これにはPerlが適しています。

ここに私の微調整したバージョンがあります:

  • 一致する文字列を持たないファイルを変更しようとしない
  • 変更されるファイルの以前のバージョンをバックアップします(.bakバージョンを作成します)
  • 変更されたすべてのファイル/行をリストし、読みやすいようにその行の古いバージョンと新しいバージョンをインデントして下に表示します

コード

find /tmp/test -type f ! -name "*.bak" -exec grep -l '/opt/gridmon' {} \; | xargs -L1 Perl -ni'.bak' -e'$old=$_; s/\/opt\/gridmon/~/g && print STDERR "$ARGV($.):\n\tOLD:$old\tNEW:$_"'

出力例

/tmp/test/test4.cfg(13):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
/tmp/test/test4.cfg(24):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
1
andy987