NimでCLIツールを書くときに使ったcligenというライブラリが非常に便利でした。
めちゃくちゃ簡単にCLIツールを作れるライブラリだと感じたので使い方と使った感想を記載します。
リポジトリ: GitHub - c-blake/cligen
検証環境
名称 | バージョン |
---|---|
Linux Mint | 19.1 |
Nim | 0.20.2 |
nimble | 0.10.2 |
インストール
以下のコマンドを実行する。
nimble install cligen
実装する
インストールしたら以下のコードを書いてみます。
proc sum(debug=false, args: seq[string]): int =
discard
when isMainModule:
import cligen
dispatch(sum)
なんとdispatch
という関数に関数名を渡すと、その関数の引数定義がそのままコマンドのオプションになるという驚異的な仕様です。
オプション引数の必須、任意についても、デフォルト引数を定義するか否か、だけで切り替わるので、覚えないといけないことが非常に少ないです。
必須オプションと任意オプションの設定
例えば以下のように、引数fooにデフォルト引数を定義すると、それに紐づくオプションは省略できます。
逆にデフォルト引数を指定しなければ、そのオプションは必須になります。
proc sum(foo=0, bar: int, args: seq[string]): int =
discard
when isMainModule:
import cligen
dispatch(sum)
引数最後にseqを定義すると、その配列引数へオプション引数に該当しないもの全てが詰められます。
コマンドの終了コードの指定
関数の戻り値がそのままコマンドの終了コードになるので、
異常系の場合は0以外を返すように実装します。
proc proc1(exitCode: int): int =
return exitCode
when isMainModule:
import cligen
dispatch(proc1)
$ nim c a.nim
$ ./a 0 ; echo $?
0
$ ./a 1 ; echo $?
1
ショートオプションの設定
引数のショートオプションは引数名の先頭1文字が採用されます。
先頭の文字が重複した場合は、あとに定義されたオプションにはショートオプションなしになります。
proc proc1(opt1 = false, opt2 = false): int =
return 0
when isMainModule:
import cligen
dispatch(proc1)
$ nim c b.nim
$ ./b -h
Usage:
proc1 [optional-params]
Options(opt-arg sep :|=|spc):
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-o, --opt1 bool false set opt1
--opt2 bool false set opt2
オプション名の変更
オプション名に別名をつけたい場合は、以下のようにすれば別の名前を決められます。
proc proc1(opt1 = false, opt2 = false): int =
return 0
when isMainModule:
import cligen
dispatch(proc1, short = {"opt2":'O'})
$ nim c b2.nim
$ ./b2 -h
Usage:
proc1 [optional-params]
Options(opt-arg sep :|=|spc):
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-o, --opt1 bool false set opt1
-O, --opt2 bool false set opt2
2019/09/21 追記:
ケバブケースのオプションで指定できるようにしたかったのですが、ロングオプション名の変更はできないように見えます。
しかし、特に指定せずともlowerCamelCaseでもkebab-caseでも指定できるようです。
Like long option keys or enum values, any unambiguous prefix is accepted. So, in the above ./cmd f -m1 would also work. This is patterned after, e.g. Mercurial, gdb, or gnuplot. Additionally, long option keys can be spelled flexibly, e.g. --dry-run or --dryRun, much like Nim's style-insensitive identifiers, but with extra insensitivity to so-called "kebab case".
ヘルプメッセージの設定
ヘルプメッセージも以下のように変更できる。
proc proc1(opt1 = false, opt2 = false): int =
return 0
when isMainModule:
import cligen
dispatch(proc1, short = {"opt2":'O'}, help = {"opt1":"オプションの説明を変更してみる"})
$ nim c b3.nim
$ ./b3 -h
Usage:
proc1 [optional-params]
Options(opt-arg sep :|=|spc):
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-o, --opt1 bool false オプションの説明を変更してみる
-O, --opt2 bool false set opt2
サブコマンドを定義する
サブコマンドの定義もdispatchMulti
関数で同様に定義できます。
ドキュメンテーションコメントを書くと、コマンドのヘルプとして出力されます。
proc proc1(args: seq[string]): int =
## proc1 は練習用のサブコマンドです。
discard
proc proc2(args: seq[string]): int =
## proc2 も練習用のサブコマンドです。
discard
proc proc3(args: seq[string]): int =
## proc3 も練習用のサブコマンドです。
discard
when isMainModule:
import cligen
dispatchMulti([proc1], [proc2], [proc3])
$ nim c c.nim
$ ./c -h
Usage:
c {SUBCMD} [sub-command options & parameters]
where {SUBCMD} is one of:
help print comprehensive or per-cmd help
proc1 proc1 は練習用のサブコマンドです。
proc2 proc2 も練習用のサブコマンドです。
proc3 proc3 も練習用のサブコマンドです。
c {-h|--help} or with no args at all prints this message.
c --help-syntax gives general cligen syntax help.
Run "c {help SUBCMD|SUBCMD --help}" to see help for just SUBCMD.
Run "c help" to get *comprehensive* help.
サブコマンドのオプション、ヘルプメッセージの変更
サブコマンドも同様にショートオプショにゃヘルプメッセージを変更できます。
proc proc1(opt1 = false, args: seq[string]): int =
## proc1 は練習用のサブコマンドです。
discard
proc proc2(args: seq[string]): int =
## proc2 も練習用のサブコマンドです。
discard
proc proc3(args: seq[string]): int =
## proc3 も練習用のサブコマンドです。
discard
when isMainModule:
import cligen
dispatchMulti([proc1, short = {"opt1":'O'},
help = {"opt1":"オプションを変更した"}],
[proc2],
[proc3])
$ nim c c2.nim
$ ./c2 proc1 -h
proc1 [optional-params] [args: string...]
proc1 は練習用のサブコマンドです。
Options(opt-arg sep :|=|spc):
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-O, --opt1 bool false オプションを変更した
サブコマンド名の変更
2019/10/11 追記
つけたいサブコマンド名がNimのモジュールの関数名と重複していて、紛らわしくて名前をつけられないケースもあるかと思います。
そういう時は cmdName
という引数で名前を上書きできます。
proc subCommandAdd(args: seq[string]): int =
discard
proc subCommandInsert(args: seq[string]): int =
discard
proc subCommandDelete(args: seq[string]): int =
discard
when isMainModule:
import cligen
dispatchMulti(
[subCommandAdd, cmdName = "add"],
[subCommandInsert, cmdName = "insert"],
[subCommandDelete, cmdName = "del"],
)
バージョン情報だけ出力するオプションの定義
2019/07/24 追記
バージョンだけ出力するオプションを追加で定義したい場合は以下のようにします。
proc proc1(args: seq[string]): int =
return 0
when isMainModule:
import cligen
clCfg.version = "0.1.0" # ここを追加した
dispatch(proc1)
$ nim c ver.nim
$ ./ver -h
Usage:
proc1 [optional-params] [args: string...]
Options(opt-arg sep :|=|spc):
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
--version bool false print version
cligenのメリット
- 覚えないといけないことが少ない
- 関数定義とimport、dispatchしかしなくてよい
- ライブラリを導入してすぐに使い始められる敷居の低さ
- 記述量が少ない
- オプション定義に必要なのはたった2行
- サブコマンドの定義が簡単
- dispatchMultiするだけで良くてとてもお手軽にサブコマンドが定義できて快適すぎる
cligenのデメリット
- ヘルプメッセージがみづらい
- 関数にドキュメンテーションコメントを書けば、ヘルプ出力時に表示されるが、表示のされ方が微妙
前述のコマンドの実行結果を引用
- 関数にドキュメンテーションコメントを書けば、ヘルプ出力時に表示されるが、表示のされ方が微妙
$ ./c2 proc1 -h
proc1 [optional-params] [args: string...]
proc1 は練習用のサブコマンドです。
Options(opt-arg sep :|=|spc):
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-O, --opt1 bool false オプションを変更した
-
## proc1 は練習用のサブコマンドです。
と関数にドキュメンテーションコメントを書いた結果が反映されているが読みづらい - ただしデフォルトの書式が読みにくいだけで、
usage
引数にテンプレート文字列を渡せば独自の書式に変更できる
まとめ
CLIツールを作成するためのライブラリのcligenの使い方について説明しました。
Nimのオプションパーサー系ライブラリの中では一番簡単に使えると感じています。
GitHubスター数もNimのオプションパーサーライブラリの中で一番多いです。
CLIツールを作るときに役立てば幸いです。
以上です。