複数ファイルの一括置換
2007-02-24
複数のファイルに含まれている文字列を一括で置換したい。 たぶんありがちなネタなんだろうけど、自力でやってみることにした。
お題
拡張子が .rb の全てのファイルに対して、
#!/usr/bin/env ruby
を
#!/usr/local/bin/ruby
に置換する。 ついでに、「ruby」という文字を「perl」に置き換える。
とりあえずの結論
find . -name '*.rb' | xargs ruby -p -i -e '
gsub(%r{^/usr/bin/env ruby$}, "#!/usr/local/bin/ruby")
gsub(%r{ruby}, "perl")
'
大切なのは
結論じゃなくてプロセス。 どうやって答えにたどり着いたかが大切。 ってことで、メモ。
やらなきゃいけないことは、たぶん2点に分けられる。
- 拡張子 .rb のファイルの一覧を抽出する
- ファイルの内容を置換する
両方を一度にやると大変なので、順番に片付けていく。
拡張子 .rb のファイルの一覧を抽出する
ls と grep を駆使すればできるかも…と一瞬思ったけど、いつも使い方が分からない find を使ってみることにした。 find linux でググって、簡単な使い方が分かった。
$ find -name '*.rb'
./misc/style/etdiary/etdiary_test.rb
./misc/style/etdiary/etdiary_style.rb
./misc/style/wiki/wiki_parser.rb
./misc/style/wiki/wiki_style.rb
(中略)
./update.rb
./index.rb
./tdiary.rb
次に、それぞれのファイルに対してコマンドを実行する方法を調べる。
$ find -name '*.rb' | ls -l
と書いてみたけどうまく動かない(ファイルのリストがパイプでまとめて渡されるので当たり前)。 調べたら xargs というコマンドを使えばよさそう。
$ find -name '*.rb' | xargs ls -l
$ find -name '*.rb' | xargs ls -l
-rwxr-xr-x 1 machu machu 3076 Feb 24 11:26 ./index.rb
-rwxr-xr-x 1 machu machu 2626 Feb 24 11:26 ./misc/convert2.rb
(中略)
-rwxr-xr-x 1 machu machu 6246 Feb 24 11:26 ./tdiary/wiki_style.rb
-rwxr-xr-x 1 machu machu 2768 Feb 24 11:26 ./update.rb
なんかそれっぽくなった。
ファイルの内容を置換する
引数で受け取ったファイルの内容を置換するプログラム。 Ruby で書いた。
ruby -e 'ARGF.each{|line|
line.gsub(%r{^/usr/bin/env ruby$}, "#!/usr/local/bin/ruby")
line.gsub(%r{ruby}, "perl")
puts line
}'
このままだと置換後の内容が標準出力に表示されるだけ。 Rubyリファレンスマニュアル - Rubyの起動を読むと、 -i オプションが使えることが分かった。
引数で指定されたファイルの内容を置き換える(in-place edit)ことを指定します。
と言うわけで、 -i オプションをつけて、さっきの find と組み合わせることで目的達成。
find -name '*.rb' | xargs ruby -i -e 'ARGF.each{|line|
line.gsub!(%r{^/usr/bin/env ruby$}, "#!/usr/local/bin/ruby")
line.gsub!(%r{ruby}, "perl")
puts line
}'
あとは ARGF の代わりに -p を使ったりとかで最適化して冒頭に戻る。