sedコマンドでディレクトリ内の全ファイルをテキスト全置換するには

単一ファイルを置換する場合

テキストファイル内を全置換するにはsedコマンドでできます。

$ sed -i 's/BEFORE/AFTER/g' FILENAME
  • s/BEFORE/AFTER/gsの意味: BEFOREAFTERに置換するというsedのコマンド。sedコマンドは置換だけではなく、行抽出や削除などいろんな処理ができる。
  • s/BEFORE/AFTER/ggの意味: BEFOREにマッチした部分をすべて置換する。gがないと、各行で1つ目のみを置換する。1つ目のみでいいケースは少ないから、置換のときはなにも考えずにgを付けるぐらいでもよい。
  • -i: 全置換してファイルを書き換える。-iがないと、置換結果を標準出力するのみで、ファイル書き換えはしない。

$ cat test.txt 
abcdefabc
abc

$ sed 's/b/B/g' test.txt # 全置換
aBcdefaBc
aBc

$ sed 's/b/B/' test.txt  # 各行1つ目だけ置換
aBcdefabc
aBc

バックアップを残したい場合

-i-i.bak のように拡張子を付けると、ファイル名の最後に.bak を付けた名前で置換前のバックアップを取ってくれます。

ディレクトリ内の全ファイルを置換したい場合

findコマンドとxargsコマンドを併用

カレントディレクトリ内のすべてのファイルを再帰的に探索して、全ファイルを置換するには、findコマンドとxargsコマンドをsedに組み合わせればできます。

$ find . -type f | xargs sed -i 's/BEFORE/AFTER/g'

findコマンドで見つけたファイルをxargsコマンドを使ってsedコマンドに渡します。

findコマンドでファイルを絞ることもできます。

$ find . -type f -name "*.txt" | xargs sed -i 's/BEFORE/AFTER/g'

ただし、この方法は、findコマンドで見つけたファイルをsedコマンドで保存しなおしますので、置換対象の文字列がなかったとしても、タイムスタンプがすべて現在時刻に置き換わります。それが気持ち悪い場合はgrepも併用するとよいです。

grepコマンドも併用

grepコマンドを併用することで、置換対象の文字列があるファイルのみをsedコマンドに渡します。

$ find . -type f -name "*.txt" | grep -rl BEFORE | xargs sed -i 's/BEFORE/AFTER/g'

grepを使う場合で、findでファイルを絞る必要がない場合はfindは不要です。

$ grep -rl BEFORE . | xargs sed -i 's/BEFORE/AFTER/g'

全置換が怖いからバックアップを残したい

sedのバックアップオプション

sedコマンドでバックアップ拡張子を指定すれば、sedコマンドに渡された全ファイルのバックアップが残されます。

$ find . -type f -name "*.txt" | grep -rl BEFORE | xargs sed -i.bak 's/BEFORE/AFTER/g'

この方法は、大量のバックアップファイルが生成されてしまって、後片付けが面倒という問題があります。置換結果の差分を見るのも面倒です。

ディレクトリごとバックアップ

全置換で大量のファイルが更新されるのがちょっと怖いという場合は、置換前にディレクトリごとコピーしてしまって、置換後にディレクトリごとdiffコマンドで見ることが私は多いです。

$ cp -rvp . ../backup

$ find . -type f -name "*.txt" | grep -rl BEFORE | xargs sed -i 's/BEFORE/AFTER/g'

$ diff -r ../backup .

gitを使っていればバックアップ不要

git管理下にあれば、バックアップとらずに git diff で置換結果を見て、もとにもどしたければ git checkout すればよいです。