For Development HEAD DRAFTSearch (procedure/syntax/module):

Next: , Previous: , Up: ライブラリモジュール - Gauche拡張モジュール   [Contents][Index]

9.25 gauche.parseopt - コマンドライン引数の解析

Module: gauche.parseopt

このモジュールでは、コマンドラインオプションをパースするための便利な方法を定義 しています。インタフェースは Perl にヒントを受けたもので、複数のオプション引数を 伴う長い形式のオプションを便利に扱うことができます。

実際、Gauche でコマンドラインオプションをパースするにはいくつかの選択肢が あります。SRFI-37 (srfi.37 - args-fold プログラム引数処理参照)では、 POSIX/GNU 互換の引数構文をパースするための関数的なインタフェースを提供しています。 SLIB は、getopt 互換のユーティリティを持っています。 要求される機能はアプリケーションごとに異なるので、 あなたの要求にフィットするものを選んで下さい。

高レベルAPI

Macro: let-args args (bind-spec … [. rest]) body …

{gauche.parseopt} このマクロはコマンドライン引数処理の最も典型的なパターンを扱います。 引数のリストargsを取り、bind-specで示される仕様をもとに コマンドラインオプションを探してその値を変数に束縛し、それから body …を実行します。

まず簡単な例を見てみましょう。このフォームが何をするか、だいたい想像できるのでは ないかと思います。(より多くの例については下の“Examples”の項を参照して ください)。

(define (main args)
  (let-args (cdr args)
      ((verbose     "v|verbose" ? "Run verbosely.")
       (outfile     "o|outfile=s{FILE}" ? "Write output to {FILE}.")
       (debug-level "d|debug-level=i{LEVEL}" 0 ? "Set debug level.")
       (help        "h|help" => (cut show-help (car args))
                    ? "Show help message and exit.")
       . restargs
      )
    ....))

(define (show-help progname)
  (print "Usage: ....")
  (pritn "Options:")
  (print (option-parser-help-string))
  (exit 0))

ローカル変数verboseは、コマンドラインオプション-v--verbose が与えられれば#tに、そうでなければ#fに束縛されます。 変数outputはオプション引数を取ると指定されており、例えば -o out.txtのようにコマンドラインに指定されれば値"out.txt"が 束縛されます。debug-levelも似ていますが、オプション引数は 整数へと変換され、またデフォルト値0が指定されています。 help節では、単に値を束縛するだけでなくアクションを起動しています。

(註: 今のところlet-args-v--vを区別しません。 -verbose--verboseも同様です。将来、getopt_long(3)と 互換になるオプションを追加するかもしれません。)

?の後の文字列はオプションのヘルプ文字列です。 option-parser-help-stringでフォーマットされたオプションのヘルプを 得ることができます。

最後のドットの後のrestargsは、オプションでないコマンドライン引数のリストを 受け取ります。

bind-specについて詳しく見てみましょう。 bind-specは次のいずれかの形式でなければなりません。

1. (var option-spec [default] [? helpstr])
2. (var option-spec [default] => callback [? helpstr])

3. (else => handler)
4. (else formals body ...)

コマンドライン引数のリストがargsに渡されると、それが option-specに基づいてパーズされます。該当するオプションが見付かれば、 変数varが次に述べる値に束縛されます:

(a) bind-specの形式が上の1. の場合:
  (a1) option-specが引数を要求しない場合は、#t。
  (a2) option-specがひとつの引数を要求する場合は、その引数の値。
  (a3) option-specがそれ以上の引数を要求する場合は、引数の値のリスト。
(b) bind-specの形式が上の2.の場合、callbackを引数の値を伴って呼び出し、その戻り値。

option-specの詳細については後で説明します。

特別な場合として、var#fとすることもできます。 その場合、値は無視されます。 callbackでの副作用だけに関心がある場合に使えるでしょう。

対応するオプションがargsに与えられなかった場合、 vardefaultが与えられていればその値に、 そうでなければ#fに束縛されます。

最後のbind-specには3か4の形式も許されます。 この節は、どのoption-specにもマッチしないコマンドラインオプションが 与えられた場合に選択されます。 3番目の形式では、handlerが3つの引数を伴って呼び出されます。 引数は、マッチしなかったコマンドラインオプション、 残りのコマンドライン引数、そして引数処理に戻るための継続手続きです。 handlerは与えられたコマンドラインオプションを処理した後、 オプション処理を続けたければ継続手続きに残りのコマンドライン引数を渡し、 オプション処理を打ちきりたければ残りのコマンドライン引数を戻り値として 返します。返された値は通常の(オプションでない)コマンドライン引数として扱われます。 4番目の形式は(else => (lambda formals body ...))の簡略表記です。

束縛リストは、最後のcdrにシンボルを持つ不完全なリストであっても良く、 その場合はコマンドライン引数の残りのリストがその変数にと束縛されます。

defaultcallbackelse節のフォーム等は varの束縛の外側のスコープで評価されることに注意して下さい。

C における典型的な getoptgetopt_long の実装とは異なり、 let-args は与えられたコマンドライン引数の順番を変えません。 オプションでない引数(ハイフンで始まらない引数)に遭遇した時点でパースを 中止します。

パーサは、ハイフン2つのみの引数 ‘--’ に遭遇すると、引数パーシングを 中止して‘--’ の後の引数のリストを返します。

全ての束縛が終了した後、body … が評価されます。 bodyは内部defineで始まっていても構いません。

内部的に、let-argsは「オプションパーザ」オブジェクトを作り、 それはlet-argsの動的エクステント中、current-option-parserパラメータで 取り出すことができます。下記参照。

Parameter: current-option-parser

{gauche.parseopt} let-argsの動的エクステント中、 このパラメータは「オプションパーザ」オブジェクトに束縛されます。 オプションパーザはコマンドライン引数をパーズして、各オプションの出現および 引数の値を保持します。 このオブジェクトは不透明なオブジェクトとして扱われなければならず、 唯一の用途はoption-parser-help-stringに渡して フォーマットされたオプションのヘルプ文字列を得ることです。

Function: option-parser-help-string :key option-parser omit-options-without-help option-indent description-indent width

{gauche.parseopt} option-parserで指定されるオプションパーザの、コマンドラインオプション ヘルプ文字列を返します。option-parserのデフォルト値は パラメータcurrent-option-parserの値なので、 let-argsで作られるオプションパーザのヘルプを let-argsの動的エクステント内で表示する場合は option-parser引数を明示的に与える必要はありません。

上のlet-argsにあるコマンドラインオプション仕様の例では、 以下のようなヘルプ文字列が生成されます。

  -v, --verbose
               Run verbosely.
  -o, --outfile FILE
               Write output to FILE.
  -d, --debug-level LEVEL
               Set debug level.
  -h, --help   Show help message and exit.

オプション指定の中でオプション引数の名前がカーリーブレースで囲んで指定されて いた場合は、その名前がヘルプ文字列に使われます (例: "o|outfile=s{FILE}")。詳しくは下の「オプション指定」を 見てください。ヘルプメッセージ中でオプション引数の名前を参照する時にも、 その名前をカーリーブレースで囲んでおくことを推奨します。 今は単にそのカーリーブレースは除かれるだけですが、 将来、よりリッチな表現を許すデバイス向けにヘルプ文字列を生成する機会があれば、 別の表示が可能になるかもしれません。

もしomit-options-without-help引数に真の値が与えられたら、 ヘルプ文字列が指定されていないオプションは返り値から除外されます。 実験的にオプションを追加して、でもユーザには広報したくない場合に便利です。 デフォルトは#fで、その場合、ヘルプ文字列が指定されていないオプションには (No help available)と表示されます。

引数option-indentdescription-indentwidthは ヘルプ文字列のフォーマットを制御します。各オプション名は option-indent文字だけ、その記述は description-indent文字だけインデントされ、 widthが全体の幅を指定します。 ヘルプ文字列はtext.fillの機能によってフォーマットされます (text.fill - テキストの再詰め込み参照)。

オプション指定

option-spec は、オプションの名前とそのオプションがどのように引数を取るか を指定する文字列です。オプションの名前には、アルファベット文字、数字、 アンダースコア、プラス記号、ハイフンが許されますが、ハイフンは最初の文字としては 使えません。すなわち、有効なオプションの名前は、#/[\w+][-\w+]*/ という 正規表現にマッチするものです。

オプションが引数を取る場合、名前の後ろに等号文字と引数の型を表現する文字を 付けることで指定できます。オプションは一つ以上の引数を取ることができます。 以下の文字がオプションの引数の型を表現するものとして認識されます。

s

文字列。

n

数値。

f

実数 (flonumに変換されます)。

i

正確な整数。

e

S式。

y

シンボル (引数はstring->symbolにより変換される)。

option-specの例を見てみましょう:

"name"

引数を取らないオプションの name を指定します。

"name=s"

オプション name は引数を一つ取り、それは文字列として渡されます。

"name=i"

オプション name は引数を一つ取り、それは正確整数として渡されます。

"name=ss"

オプション name は引数を二つ取り、両方とも文字列です。

"name=iii"

オプション name は3つの整数の引数を取ります。

"name=sf"

オプション name は2つの引数を取ります。一つ目は文字列で、 二つ目は数値です。

各オプション引数文字の後に、カーリーブレースで囲んだオプション引数名を書いておくことが できます。オプション引数名はコマンドライン引数のパーズには影響を与えませんが、 ヘルプ文字列を生成する時に使われます (上のoption-parser-help-stringを参照)。

--file=s{FILENAME}
--origin=f{XCOORD}f{YCOORD}f{ZCOORD}

オプションにいくつかの別名がある場合は、"|" でつなげて書いておくことができます。 例えば"h|help"というoption-specは "h"にも"help"にもマッチします。

コマンドラインでは、オプションは一つか二つのハイフンに続いて与えられます。 オプションの引数は、オプションそのものと等号記号でつながれていても構いません。 例えば、以下の全てのコマンドライン引数は、オプションの仕様、"prefix=s" に マッチします。

-prefix /home/shiro
-prefix=/home/shiro
--prefix /home/shiro
--prefix=/home/shiro

オプションが一文字からなり、引数を取る場合、 最初の引数はスペースなしでオプション文字の直後に置かれても構いません。 例えばオプション仕様"I=s"は、以下のコマンドライン引数のいずれも認識します。

-I/foo
-I /foo
-I=/foo
--I/foo
--I /foo
--I=/foo

長いオプション名と、一文字のオプション+引数、の間で曖昧性が生じた場合は、 長いオプションの方が優先されます。例えば"long"および"l=s"という オプション仕様があった場合、コマンドライン引数-long-lオプション+引数ongではなく、ひとつのオプション-longと 解釈されます。

エラー処理

Condition Type: <parseopt-error>

{gauche.parseopt} let-argsが、option-specに従わない引数を見つけた場合は、 コンディションタイプ<parseopt-error>のエラーを投げます。 例えば、必須のオプション引数が与えられていなかったり、異なる型であった 場合などです。

(let-args '("-a" "foo") ((a "a=i")) ; option a requires integer
  (list a))
 ⇒ parseopt-error

このコンディションはあくまでargsに渡された引数をパーズする際に発生するものです。 option-specが不正であった場合は通常のエラーが投げられます。

これはgauche-installスクリプトから取った例です。 modeオプションは8進数のオプション引数を取るので、コールバック 手続きを使って変換しています。また、認識できないオプションをelse節で 処理しています。

(define (main args)
  (let-args (cdr args)
      ([mkdir   "d|directory"
                ? "Creates directories listed in the arguments. \
                   (3rd format only)."]
       [mode    "m|mode=s{MODE}" #o755 => (cut string->number <> 8)
                ? "Change mode of the installed file."]
       [owner   "o|owner=s{OWNER}"
                ? "Change owner of the installed file(s)."]
       [group   "g|group=s{GROUP}"
                ? "Change group of the installed file(s)."]
       [csfx    "C|canonical-suffix"
                ? "If installed file has a suffix *.sci, replace it for \
                   *.scm.   This is Gauche specific convention."]
       [srcdir  "S|srcdir=s{DIR}"
                ? "Look for files within {DIR}; useful if VPATH is used."]
       [target  "T|target=s{DIR}"
                ? "Installs files to the {DIR}, creating paths if needed. \
                   Partial path of files are preserved. (4th format only)."]
       [utarget "U|uninstall=s{DIR}"
                ? "Reverse of -T, e.g. removes files from its destination."]
       [shebang "shebang=s{PATH}"
                ? "Adds #!{PATH} before the file contents. \
                   Useful to install scripts."]
       [verb    "v|verbose" ? "Work verbosely"]
       [dry     "n|dry-run" ? "Just prints what actions to be done."]
       [sprefix "p|strip-prefix=s{PREFIX}"
                ? "Strip prefix dirs from FILEs before \
                  installation. (4th/5th format only)."]
       [#f      "h|help" => usage ? "Show this help message."]
       [#f      "c" ? "This option is ignored.  Recognized for the \
                       compatibility."]
       [else (opt . _) (print "Unknown option : " opt) (usage)]
       . args)
    ...)
  )

次の例はelse節の使い方を示す小さなプログラムです。 コマンドラインオプションを変数rに集めてゆきますが、 オプション -c に出会うとオプション処理を中止して残りを restargsへと渡します。

(use gauche.parseopt)

(define (main args)
  (let1 r '()
    (let-args (cdr args)
      ((else (opt rest cont)
         (cond [(equal? opt "c") rest]
               [else (push! r opt) (cont rest)]))
       . restargs)
     (print "options: " (reverse r))
     (print "restargs: " restargs)
     0)))

上のスクリプトの実行例です (exampleというファイル名で保存されていると します)。

$ ./example -a -b -c -d -e foo
options: (a b)
restargs: (-d -e foo)
$ ./example -a -b -d -e foo
options: (a b d e)
restargs: (foo)

低レベルAPI

内部的に、let-argsはoption parserオブジェクトと option specオブジェクトを作ってコマンドライン引数をパーズします。 これらのクラスの詳細は公開していませんが、それらを扱うためのいくつかの手続きが 提供されています。独自のパーザを作りたい時に便利です。

Function: make-option-spec spec-string :key help-string default handler

{gauche.parseopt}

Function: build-option-parser specs :key fallback

{gauche.parseopt}

Function: get-option-spec option-parser option-name

{gauche.parseopt}

Function: option-spec-appeared? optspec

{gauche.parseopt}

Function: option-spec-value optspec

{gauche.parseopt}

非推奨API

以下のマクロはlet-argsに置き換えられました。 新規コードでは使わないでください。

Macro: parse-options args (option-clause …)

{gauche.parseopt} Deprecated. args は、コマンドライン引数のリストを含む式です。 このマクロは、コマンドラインオプション(‘-’ で始まる引数)をスキャンし、 option-clause の指定に従って処理し、残りの引数を返します。

それぞれの option-clause は、option-spec とそのアクションのペアで 構成されます。

与えられたコマンドラインオプションが option-spec の一つにマッチすると、 関連付けられたアクションが評価されます。アクションは以下のフォームの一つです。

bind-spec body

bind-spec は、ラムダリストのような変数の正しいリストかドット対リストです。 オプションの引数は bind-spec に束縛され、body … が評価されます。

=> proc

コマンドラインオプションが option-spec にマッチすると、 proc がオプションの引数のリストとともに呼び出されます。

シンボル elseoption-spec の位置にある場合、その節は、 与えられたコマンドラインオプションにマッチする他のオプション節が ない場合に選択されます。その節には3つの“引数”が関連付けられます。 それらは、マッチしなかったオプション、引数の残り、オプションパーサを 表す手続きです。

Macro: make-option-parser (option-clause …)

{gauche.parseopt} Deprecated. これは低レベルのインタフェースです。option-clause は、 parse-options と同じです。このマクロは、コマンドラインオプションを 後でパースするために使うことができる手続きを返します。

返される手続きは、一つの必須の引数と一つのオプション引数を取ります。 必須の引数は、与えられたコマンドライン引数としての文字列のリストです。 オプションの引数は、三つ以上の引数を取る手続きで、それが与えられると 手続きはそれが else オプション節のボディであるかのように使われます。


Next: , Previous: , Up: ライブラリモジュール - Gauche拡張モジュール   [Contents][Index]


For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT