カレントディレクトリ以下の全てのファイルの文字列を置換して上書き保存する シェルスクリプトメモ(1)

find 検索開始ディレクトリ オプション -exec perl -p -i.bak -e 's/検索文字列/置換文字列/g' {} \;

使用例

find . -type f -exec perl -p -i -e 's/<html>/<html lang="ja">/g' {} \;

Perlの各オプションの意味

項目名 意味
-p 入力ファイルから1行づつ取り出して処理し、表示する。
-i ファイルを修正する。拡張子を指定すると、その拡張子でバックアップを取ってくれます。
-e 直後に続く部分をスクリプトとして処理する。
s/検索文字列/置換文字列/g 文字列の置換。検索文字列を置換文字列に置き換える。最後の「 g 」を指定すると、1行に複数検索文字列がある場合、全て置換してくれる。なければ最初の1個だけ置換。


毎回長いコマンドを打つのはめんどくさいので、シェルスクリプトにまとめて、/usr/local/binに置いておいた。

myreplace.sh(下に改良版有り)
#!/bin/sh
# 入力チェック(シェルスクリプト基本リファレンスp11)
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
    echo "Usage:$0 regexp string [filename]"
    exit 1
fi

if [ $# = 2 ]
then
# カレントディレクトリ以下の全てのファイル内の文字列を置換して上書き保存する
    exec find . -type f -exec perl -p -i.bak -e "s/$1/$2/g" {} \;
else
# 指定されたファイル内の文字列を置換して上書き保存する
    exec perl -p -i.bak -e "s/$1/$2/g" $3
fi

追記(8月10日)

myreplace.shが思ったように動かなかった。具体的な不満点と、どのように解決したかを挙げると、大体こんな感じ。

  • 繰り返し実行すると、バックアップファイルが大量に作られてしまう(例:hoge.html.bak.bak.bak ...)
    • Perlの-i.bakを使う代わりに、cpでバックアップファイルが上書き保存されるようにした
  • 指定したディレクトリ以下のファイルが再帰的に全て検索されてしまう
    • findに「-maxdepth 1」オプションを付けて、指定したディレクトリだけ検索されるようにした
  • .htmlファイルだけを検索して、置換できれば良かったのに、全てのファイルについて実行してしまう
    • findに「-name '*.html'」オプションを付けて、.htmlファイルだけ検索されるようにした
myreplace.sh(改良版)
#!/bin/sh
### HTMLファイルの内容を正規表現で置換する

# 入力チェック(シェルスクリプト基本リファレンスp11)
if [ $# -lt 2 -o $# -gt 3 ]; then
    echo "Usage:$0 regexp string [filename]"
    exit 1
fi

if [ $# = 2 ]
then
    # .htmlファイルのbackupをとる(シェルスクリプト基本リファレンスp96)
    for file in *.html
    do
	cp -p "$file" "$file".bak
    done
    # カレントディレクトリの.htmlファイル内の文字列を置換して上書き保存する
    exec find . -maxdepth 1 -name '*.html' -type f -print0 | xargs -0 perl -p -i -e "s/$1/$2/g"
else
# 指定されたファイルのbackupをとる
    cp -p "$3" "$3".bak
# 指定されたファイル内の文字列を置換して上書き保存する
    exec perl -p -i -e "s/$1/$2/g" $3
fi

参考

tips - 18 til i die (another phase)(findの用法が参考になった)

追記(2007年9月5日(水))

上記のシェルスクリプト中の置換処理部分で Perl を使ってるけど、Ruby でもよかった気がする。Perl はまったく勉強してないし…。

$ ruby -pi.bak -e "gsub(/置換対象の文字列/, '置換後の文字列')" 置換対象のファイル名

追記(2007年9月7日(金))

ruby で書き直したので、メモしておく。

#!/bin/sh
### HTMLファイルの内容を正規表現で置換する

# 入力チェック(シェルスクリプト基本リファレンスp11)
if [ $# -lt 2 -o $# -gt 3 ]; then
    echo "Usage:$0 regexp string [filename]"
    exit 1
fi

if [ $# = 2 ]
then
    # カレントディレクトリの.htmlファイル内の文字列を置換して上書き保存する
    ruby -Ks -pi.bak -e "gsub(/$1/, '$2')" *.html
else
# 指定されたファイル内の文字列を置換して上書き保存する
    ruby -Ks -pi.bak -e "gsub(/$1/, '$2')" $3
fi