rawk.rb

30分プログラム、その22。

RubyによるAWKっぽいプログラム。

次のようなls.rawkを用意する。

BEGIN{
    $sum = 0
    $count = 0
    puts "BYTES\tFILE"
}

rule {|*vals|
    unless vals[0] == 'total'
      $sum += vals[4].to_i
      $count += 1
      print vals[4],"\t",vals[8],"\n"
    end
}

END{
    puts "Total: #{$sum} bytes (#{$count} files)"
}

すると、次のようにできる。

$ ls -l | rawk -f ls.awk 
BYTES   FILE
1609575 bible
770     busybox.pl*
102     wcat.rb
....
....
682     xgrep.rb
Total: 1653125 bytes (42 files)

ちなみに、同じプログラムをAWKで書くと次のようになる。

BEGIN{
    print "BYTES\tFILE"
}

{
    sum += $5
    count ++
    print $5,"\t",$9,"\n"
}

END{
    print "Total:,",sum,"bytes (",count," files)"
}
#!/opt/local/bin/ruby -w
require 'optparse'

Table = []
def rule(regexp=/.*/,&proc)
  Table.push [regexp,proc]
end

def rawk(io,table)
  io.each do|line|
    Table.each do|regexp,proc|
      if line =~ regexp then
        proc.call(*line.split)
      end
    end
  end
end

loaded = false
opt = OptionParser.new
opt.on('-f FILENAME'){|filename| 
  load filename
  loaded = true
}

opt.parse! ARGV

unless loaded
  eval ARGV.shift
end

if ARGV.empty?
  rawk(STDIN,Table)
else
  ARGV.each do|filename|
    File.open(filename){|io| rawk(io,Table) }
  end
end
  • 素直にAWKで書いたほうがシンプルじゃね?
  • AWKは書いた順が大切なので、Hashは使えない
  • RubyにはBEGIN/ENDがもう用意されている。初めて使ったよ
  • わざわざグローバル変数($sum,$count)を使うのがよくない