37
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-07-22

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

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".

ヘルプメッセージの設定

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

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

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

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

以上です。

37
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?