@kyanny's blog

My thoughts, my life. Views/opinions are my own.

気の利いたコマンドラインオプションの解釈の仕方

rails-log-block-grep を書いてたときに、コマンドラインオプションの扱いにも「おもてなしの心」が大事だなと思うことがあったので書いておく。

grep(1) には --color というオプションがあって詳細は man 1 grep 読んでもらうとして、 [always|never|auto] のいずれかを指定するか、値は省略することもできる(その場合 auto がデフォルト)この挙動を Ruby 標準添付の OptionParser でどう書けば実現できるか、という話。

ふつうに opts.on を並べて opts.parse! してから pattern = ARGV.shift などとすると、コマンドライン引数の誤解釈がおこってしまい正常に動作しない。 --color オプションの引数を任意(必須ではない)にするだけではダメで、 parse! 後に ARGV が壊れていないことを確認するか、 --color オプションの取りうる値を列挙しておいてそれの引数を食わないようにさせればよい。

  opts.on('--colour [always|never|auto]', '--color [always|never|auto]', ["always", "never", "auto"], 'colorize matching string with GREP_COLOR variable. see grep(1) manpage.'){ |flag|
    options['color'] = flag
  }
  
  begin
    opts.parse!
  rescue OptionParser::InvalidArgument # --color without WHEN
    options['color'] = "auto"
  end
  
  # ARGV check - must include at least 2 args
  unless ARGV.length >= 2
    puts opts.help
    exit!
  end

これで、 --color patern production.log でも --color always pattern production.log でも動くようになった。 -- を挟んでオプションの終端を明示してやればいいんだけど、利用者にそういう作法を強いないほうが使いやすくて良いプログラムだと思う。プログラムにはなるべく空気を読んで、細かいオプションや引数を覚えていなくても使えるようで有って欲しい。そしておれはもっぱら自分自身のためにこういうプログラムを書くので、空気を読むプログラムを書いて楽をしていきたいと思う。