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

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 => fallback)
4. (else formals body ...)

コマンドライン引数のリストがargsに渡されると、それが option-specに基づいてパーズされます。該当するオプションが見付かれば、 オプションの「値」が次のとおり決定されます。

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

オプションには、「単数」、つまりたかだか一回までの指定が有効か、 「複数」、つまりコマンドラインに複数現れたらそれを全て考慮されるかがあります。 単数オプションでは、varは上で決定される値がそのまま束縛されます。 複数オプションでは、varは上で決定される値のリストが束縛されます。

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

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

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

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

オプションの名前の後に*を置くと、オプションが複数であること、 つまり複数回指定できて、その値のリストを受け取れることを示します。 例えばI|incdir*=sという指定は、 -I-incdirオプションがコマンドラインで複数回指定できて、 その最終的な値は与えられた文字列引数のリストになります。

コマンドラインでは、オプションは一つか二つのハイフンに続いて与えられます。 オプションの引数は、オプションそのものと等号記号でつながれていても構いません。 例えば、以下の全てのコマンドライン引数は、オプションの仕様、"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: " 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はオプションパーザオブジェクトと オプション指定オブジェクトを作ってコマンドライン引数をパーズします。 これらのクラスの詳細は公開していませんが、それらを扱うためのいくつかの手続きが 提供されています。独自のパーザを作りたい時に便利です。

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

{gauche.parseopt} option-specにあるオプション指定文字列(上の「オプション指定」の項参照)を解析し、 オプション指定オブジェクトを作って返します。 オプション指定オブジェクトは不透明なオブジェクトとして扱ってください。

キーワード引数はlet-args形式のbind-specで指定するものに相当します。

Function: build-option-parser specs :key fallback

{gauche.parseopt} オプション指定オブジェクトのリストを取り、オプションパーザオブジェクトを作って返します。 オプション指定オブジェクトはmake-option-specで作ることができます。

作られたオプションパーザオブジェクトはrun-option-parserに渡して、 コマンドライン引数を処理するのに使えます。

省略可能なfallback引数は、渡す場合は手続きでなければなりません。 それがいつどのように呼ばれるかはrun-option-parserを参照してください。

Function: run-option-parser option-parser args :optionap fallback

{gauche.parseopt} 文字列のリストで表されたコマンドライン引数のリストvarを、 オプションパーザオブジェクトoption-parserによって処理します。

argsの中にコマンドラインオプションが含まれていればそれを処理し、 その値を該当するオプション指定オブジェクトに保存します。 そしてオプションとその引数を取り除いた残りのコマンドライン引数リストを返します。

この手続きは、argsの要素を順に見てゆきます。 引数が-で始まっていたら、それをコマンドラインオプションとみなし、 option-parserの持つオプション指定オブジェクトに該当するものを探します。 ただし、--だけの引数は特別で、コマンドラインオプションの終了とみなされ、 それ以降の引数リストが直ちに返されます。

マッチするオプション指定があれば、オプションおよび(そのオプションが引数を取るなら) オプション引数がargsから除かれ、オプションの値が次のとおり決められます。

  • オプション指定がハンドラを持っている場合は、オプション引数を引数にしてハンドラが呼ばれ、 その戻り値がオプションの値となります。
  • そうでなく、オプションが引数を取らない場合は、#t
  • そうでなく、オプションが一つだけ引数を取る場合は、その与えられた引数。
  • そうでなければ、与えられた引数のリスト。

さらに、オプションが複数回指定可能な場合は、オプションの値がリストにまとめられます。 複数回指定不可の場合、最後の値が最終的なオプションの値となります。

オプションに見えるコマンドライン引数がオプション指定のいずれにもマッチしない場合、 fallback手続きが呼ばれます。fallback手続きは、省略可能なfallback引数に 与えられていればそれを、そうでなければbuild-option-parserに与えられた fallbackが呼ばれます。fallback手続きは3つの引数を取ります: 認識されなかったオプション引数 (先行するハイフンを除いたもの)、 その後に続く引数リスト、そして、処理を続けるための継続手続きです。

fallback手続きは、好きなように未知のオプションを処理できます。 fallback手続きが返した値がそのままrun-option-parserの返り値となります。 もしfallback手続きが残りの引数リストに対してコマンドライン引数処理を継続したい場合は、 残りの引数リストを引数として継続手続きを末尾呼び出ししてください。

run-option-parserにもbuild-option-parserにもfallback手続きが 与えられなかった場合はデフォルトの手続きが呼ばれます。それは “unrecognized option”をメッセージとする<parseopt-error>エラーを 投げます。

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

{gauche.parseopt} option-parserの持つオプション指定オブジェクトから、option-name にマッチするものを返します。run-option-parserを実行した後、 オプションの値を取り出すためにはオプション指定オブジェクトを得る必要があります。

option-nameにマッチするオプション指定が無い場合は#fが返されます。

Function: option-spec-appeared? optspec

{gauche.parseopt} オプション指定optspecで表されるオプションがコマンドライン引数に現れたかどうかを 真偽値で返します。これはrun-option-parser呼び出しの後で呼ばなければなりません。

Function: option-spec-value optspec

{gauche.parseopt} オプション指定optspecの値を返します。 これはrun-option-parser呼び出しの後で呼ばなければなりません。

オプションの値は次のとおり決定されます。

  • オプションがコマンドライン引数リストに現れなければ、 make-option-specに与えたデフォルト値。
  • オプションがコマンドライン引数リストに現れた場合は、 次のいずれか:
    • オプションが引数を取らない場合は#t
    • オプションが引数をひとつ取る場合は、与えられた引数。
    • オプションが複数の引数を取る場合は、与えられた引数のリスト。

    さらに、オプションが複数回指定可能な場合は、 全てのオプションの値がコマンドラインに出現した順でリストにまとめられます。 そうでない場合は、最後のオプションの値が保持されます。

非推奨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 オプション節のボディであるかのように使われます。



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