Python風getopt
30分プログラム、その165。Python風getopt。
昔何かでPythonのgetoptは実に使いやすい、という記述を読んだ気がするので、Rubyに適当に移植してみる。
どう使いやすいかというと、RubyのOptionParserは次のように書く。
require 'optparse' option = [] opt = OptionParser.new opt.on('-a') {|v| option['a'] = true } opt.on('-b') {|v| option['b'] = true } p opt.parse!(ARGV) p option # ruby sample.rb -a foo bar -b baz # => ["foo","bar","baz"] # {"a"=>true, "b"=>true}
これが、Pythonのgetoptだと
import getopt optlist, args = getopt.getopt(sys.argv[1:], 'ab') print args print optlist # pthyon sample.py -a foo bar -b baz # => ["foo","bar","baz"] # [('-a',''),('-b','')]
Pythonのgetoptだと、OptionParserを作る必要もない上に、オプションを覚えておく変数を自分で作らなくてもいい。
その代りRubyのOptionParserよりも機能が劣るけれど、さっと書くだけのプログラムならgetopt程度で十分。
なので、getoptをRubyに移植。自前でコマンドライン解析を書くと死にそうだったので、OptionParserに丸投げしてる。
使い方
連想リストはあまり好きじゃないので、ハッシュに変更した。
irb> require 'getopt' # 短いオプションの場合 irb> args = '-a -b -cfoo -d bar a1 a2'.split => ["-a", "-b", "-cfoo", "-d", "bar", "a1", "a2"] irb> optlist,args = getopt(args,'abc:d:') irb> optlist => {"a"=>true, "b"=>true, "c"=>"foo", "d"=>"bar"} irb> args => ["a1", "a2"] # 長いオプションの場合 irb> args = '--condition=foo --testing --output-file abc.def -x a1 a2'.split => ["--condition=foo", "--testing", "--output-file", "abc.def", "-x", "a1", "a2"] irb> optlist,args = getopt(args,'x',['condition=','output-file=','testing']) irb> optlist => {"x"=>true, "condition"=>"foo", "output-file"=>"abc.def", "testing"=>true} irb> args => ["a1", "a2"]
ソースコード
#! /opt/local/bin/ruby -w # -*- mode:ruby; coding:utf-8 -*- # # getopt.rb - Python's getopt for Ruby # # Copyright(C) 2007 by mzp # Author: MIZUNO Hiroki <hiroki1124@gmail.com> # http://mzp.sakura.ne.jp/ # # Timestamp: 2007/10/26 21:08:00 # # This program is free software; you can redistribute it and/or # modify it under the same terms as Ruby itself. # require 'optparse' def getopt(args,options,long_options=[]) parser = OptionParser.new option_result = {} # 短いオプション(-a,-b VAL)の追加 # a,b:のように書く options.scan(/.:?/).each{|option| # オプション名 name = option[0].chr # パーサに渡す文字列 parse_str = '-' + option.sub(/:\Z/,' VAL') parser.on(parse_str){|value| option_result[name] = value } } # 長いオプション名(--foo,--bar=VAL)の追加 # foo,bar=のように書く long_options.each{|option| # オプション名とパーサ用文字列 name = option.sub(/=\Z/,'') parse_str = '--' + option.sub(/=\Z/,'=VAL') parser.on(parse_str){|value| option_result[name] = value } } parsed = parser.parse args [option_result,parsed] end if __FILE__ == $0 then args = '-a -b -cfoo -d bar a1 a2'.split optlist,args = getopt(args,'abc:d:') p optlist p args # optlist,args = getopt('-a -b -cfoo -d bar --test --output-file=a a1 a2'.split, #n 'abc:d:', # ['test','output-file=']) # p optlist # p args end