sedコマンドでディレクトリ内の全ファイルをテキスト全置換するには
単一ファイルを置換する場合
テキストファイル内を全置換するにはsed
コマンドでできます。
$ sed -i 's/BEFORE/AFTER/g' FILENAME
s/BEFORE/AFTER/g
のs
の意味:BEFORE
をAFTER
に置換するというsed
のコマンド。sed
コマンドは置換だけではなく、行抽出や削除などいろんな処理ができる。s/BEFORE/AFTER/g
のg
の意味: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
すればよいです。