複数ファイルの一括置換
複数のファイルに含まれている文字列を一括で置換したい。 たぶんありがちなネタなんだろうけど、自力でやってみることにした。
お題
拡張子が .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 を使ったりとかで最適化して冒頭に戻る。