Edited at

NimのCLIツール作成用ライブラリcligenがとても便利

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以外を返すように実装します。


a.nim

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文字が採用されます。

先頭の文字が重複した場合は、あとに定義されたオプションにはショートオプションなしになります。


b.nim

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


オプション名の変更

オプション名に別名をつけたい場合は、以下のようにすれば別の名前を決められます。


b2.nim

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


ヘルプメッセージの設定

ヘルプメッセージも以下のように変更できる。


b3.nim

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関数で同様に定義できます。

ドキュメンテーションコメントを書くと、コマンドのヘルプとして出力されます。


c.nim

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.


サブコマンドのオプション、ヘルプメッセージの変更

サブコマンドも同様にショートオプショにゃヘルプメッセージを変更できます。


c2.nim

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/07/24 追記

バージョンだけ出力するオプションを追加で定義したい場合は以下のようにします。


ver.nim

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ツールを作るときに役立てば幸いです。

以上です。