ディレクトリ構造を保ったままフィルタ

ファイルをスクリプトで加工する、なんてことはよくあることなんだけど、最近、ディレクトリ以下のファイルに対して処理をして、別のディレクトリに出力したいってケースが多かったりする。
たとえば、

from/foo/00
from/foo/01
from/bar/00
from/bar/01

ていうファイル群を処理して、

to/foo/00
to/foo/01
to/bar/00
to/bar/01

に出力したい。
いまいちうまい方法が思いつかなかったから、適当に Ruby で汎用的なスクリプトを組んでみたんだけど、なんかいい方法ってないのかな。
ちなみにこう使う。

./dir_filter.rb -f from -t to -- grep -E -v '^\s*$/'

この場合、from ディレクトリ中のファイルの空白行が削除されて to に出力される。

#!/usr/bin/env ruby

require 'pathname'
require 'shell'
require 'optparse'

def main(from, to, debug, commands)
   files = []
   from.find do |path|
      files << path if path.file?
   end

   if debug > 0
      STDERR.puts "from_dir = #{from.to_s}"
      STDERR.puts "to_dir = #{to.to_s}"
   end

   files.sort.each do |file|
      dest = to + file.relative_path_from(from)
      dest.parent.mkpath

      if debug > 0
         STDERR.puts "input = #{file.to_s}"
         STDERR.puts "output = #{dest.to_s}"
      end

      Shell.new.transact do
         system(*commands) < file.to_s > dest.to_s
      end
   end
end

if $0 == __FILE__
   from = nil
   to = nil
   debug = 0

   opt = OptionParser.new
   opt.on('-t', '--to TO', String) {|v| to = Pathname.new v}
   opt.on('-f', '--from FROM', String) {|v| from = Pathname.new v}
   opt.on('-d', '--debug') { debug += 1 }
   opt.parse!(ARGV)

   unless from and to and ARGV.length > 0
      puts opt.help
      exit
   end

   main(from, to, debug, ARGV)
end