Pythonでのオプションパースのおはなし
2009-02-14 - ⧖ 4.0 min私は仕事でよくPythonで小さなツールを作成します。ログ解析やログ収集、 日報の加工や共有サーバの自分のアカウントのバックアップ等々。 その際、ツールにコマンドオプションを渡すことはよくあるシチュエーションです。 今日はPythonでのオプションパースのお話を少し。
例として、-tオプションで時刻表示、-vでバージョン表示、 その他文字列指定でファイル内容を表示する(catコマンドみたいな感じ)、 コマンドを作成してみます。
初心者またはものぐさな人向け(sys.argv)
コマンド引数にアクセスできるsys.argvを直接解析すれば 簡易のオプションパーサを作れます。(これをオプションパーサとは言わない気がするが)
#!/usr/bin/env python
__version__ = '0.0.1'
import sys
import time
def usage ():
print "usage: magic [OPTION]"
print "[OPTION]"
print " -h : just now"
print " -t : time print"
print " -v : version print"
print ""
if __name__ == '__main__' :
if len (sys .argv ) == 1 :
sys .exit ()
if sys .argv [1 ] == '-h' :
usage ()
elif sys .argv [1 ] == '-v' :
print "this tool's version is %s" % __version__
elif sys .argv [1 ] == '-t' :
print time .ctime ()
else :
print open (sys .argv [1 ]).read ()
煩雑すぎて、逆に初心者にはお奨めできないかも。 sys.argvを直接解析していくのは、一撃必中ツール (オプションが少ない時やツールを作った人しか使わない時)に 限られると思います。 より現実的な方法としては、getoptモジュールやoptparseモジュール を使う方法があります。
古参プログラマ向け(getopt)
getoptモジュール は、UnixのgetoptコマンドまたはC標準関数のgetopt()に 似たインターフェースを提供します。
例を示します。
#!/usr/bin/env python
__version__ = '0.0.1'
import getopt
import sys
import time
def usage ():
print "usage: magic [OPTION]"
print "[OPTION]"
print " -h : just now"
print " -t : time print"
print " -v : version print"
print ""
sys .exit ()
if __name__ == '__main__' :
try :
shortopt = "hvt"
longopt = ["help" , "version" , "time" ]
opts , args = getopt .getopt (sys .argv [1 :], shortopt , longopt )
except getopt .GetoptError :
usage ()
for o , a in opts :
if o in ('-v' , '--version' ):
print "this tool's version is %s" % __version__
elif o in ('-h' , '--help' ):
usage ()
elif o in ('-t' , '--time' ):
print time .ctime ()
for f in args :
print open (f ).read ()
getoptを使うのは開発環境のPythonのバージョンが古い等で optparseモジュールが使えない場面くらいでしょうか。 optparseが使えるのであればgetoptモジュールを使う必要性はあまりないと思います。
ニュータイプ向け(optparse)
より高機能なオプションパーサとして、 optparseモジュール が用意されています。 バージョン2.3から使えます。
例を示します。
#!/usr/bin/env python
__version__ = '0.0.1'
from optparse import OptionParser
import time
if __name__ == '__main__' :
p = OptionParser (version = "ver:%s" % __version__ )
p .add_option ('-t' , '--time' , action = 'store_true' ,
help = "time print just now." )
opts , args = p .parse_args ()
if opts .time :
print time .ctime ()
for f in args :
print "file: " , f
print open (f ).read ()
これからのPythonistaはoptparseモジュールを使うべきです。 ヘルプ表示生成の容易さやパーサ拡張も行えます。
一歩先へ(optcompleteを使う)
optparseモジュールを便利に使うことができるエクステンションモジュールとして optcompleteモジュール があります。
optcompleteモジュールはoptparseモジュールで作成したパーサをもとに、 bashコンプリーションを便利に使うことができます。
まずはインストール。
$ wget http://furius.ca/downloads/optcomplete/releases/optcomplete-1.2.tar.bz2
$ tar xjf optcomplete-1.2.tar.bz2
$ cd optcomplete-1.2
$ python setup.py install
$ cp -p etc/optcomplete.bash /etc/bash_complete.d/optcomplete
では、先ほどのoptparseの例をもとにoptcompleteを使ってみます。
#!/usr/bin/env python
__version__ = '0.0.2'
from optparse import OptionParser
import time
import optcomplete
#optcomplete.debugfn = '/tmp/optcomplete.log'
if __name__ == '__main__' :
p = OptionParser (version = "ver:%s" % __version__ )
p .add_option ('-t' , '--time' , action = 'store_true' ,
help = "time print just now." )
optcomplete .autocomplete (p )
opts , args = p .parse_args ()
if opts .time :
print time .ctime ()
for f in args :
print "file: " , f
print open (f ).read ()
使い方としては、parse_args()をコールする前に、 optcomplete.autocomplete(parser-object)をコールします。 使い方としてはこれだけです。 optcomplete.debugfn で動作ログの出力先を指定できます。
実際に使ってみると以下のようにファイル名の補完とともに コマンドオプションも補完してくれるようになります。 completeコマンドはログインの度に打つ必要があるので、 $HOME/.bashrcファイルあたりに追記しておくとよいと思います。
$ cp optcomp-sample.py /usr/local/bin/optcamp-sample
$ chmod 755 /usr/local/bin/optcamp-sample
$ complete -F _optcomplete optcomp-sample
$ optcomp -sample
--help -h optcomp-sample.py
--time -t optparse-sample.py
--version getopt-sample.py sysargv.py
$
最後に
その他の気になるエクステンションモジュールとしては、 CmDO や optik 、 mandy 、 simpleopt あたりでしょうか。 次回紹介できる機会があれば紹介したいと思います。