やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

【サンプルコード有り】ElixirのOptionParserの入門と使い方

OptionParserとCLI

最近はこってりした記事を書いてばかりなので
久々にあっさりした記事を書こうと思います

CLIって何

ここらへんは自己満程度の部分でメインはOptionParserの使い方のところなので
そんなもん知っとるわという方は飛ばしてください

Command Line Interface
(ターミナルとかiterm2とかpowershellやら)

いわゆる黒い画面です

cd ____
ls -a

とかをやる画面です。最近の悩みはcdと叩いた後に必ずlsも叩いてしまうこと

OptionParserって何(2回目

elixirでコマンドライン解析をよしなにやってくれるモジュールです
コマンドラインに入力された引数を解析して対象のモジュールの関数に渡すことが可能です
escriptたるものを使用してbuildすることでプロンプト(iex)を実行していない状態でも対象の関数を呼び出す際に
OptionParserを使うことで引数をよしなに渡すことができます

いつものiexからの実行

#まずプロンプト(iex)を立ち上げないといけない
$ iex -S mix
iex> Sample.say_hello("hello world")
hello world
:ok

escriptでbuildしてOptionParserで引数を渡して実行

#そのままコマンドラインから実行可能
#第1引数にメッセージ, 第2引数に出力する回数
$ ./project_name "hello world" 2
hello world
hello world
:ok

OptionParserの使い方

個人で色々試していてOptionParserの動き方がよく分からなかったので
自分のメモがてらまとめておきます

基本的な使い方は以下の通り

#第1引数に渡したい引数をリストで渡す
#strictには第1引数に与えた要素がそれぞれ何の型なのかを記述する(下で触れる)
OptionParser.parse([], strict:[])

["--string"]のように「--」を付与することでbooleanとして判定させることができる
["string"]の場合はstringの判定になる模様

戻り値は3つのリストを含むタプルになっている

{[ ], [ ], [ ]}

解析に成功したデータは第1のリストに格納される 解析されなかったデータは第2のリストに格納されて返ってくる
第3リストには不正な型の宣言があった場合にnilvalueにもつmapが返ってくる

非推奨パターン(strictを指定しない)

boolean形式の場合は解析が通るが非推奨だと怒られる
stirng形式の場合には解析が通らずに第2リストに格納されている

#boolean形式で渡す
iex(n)> OptionParser.parse(["--debug"])
warning: not passing the :switches or :strict option to OptionParser is deprecated
{[debug: true], [], []}

#string形式で渡す
iex(n)> OptionParser.parse(["debug"])
warning: not passing the :switches or :strict option to OptionParser is deprecated
{[], ["debug"], []}

ちゃんとstrictを書くパターン

boolean

#boolean形式であることを明示的にstricrtで伝える
OptionParser.parse(["--debug"], strict: [debug: :boolean])

#警告もなく無事に解析を通った
{[debug: true], [], []}

string

#string形式であることを(ry
iex(5)> OptionParser.parse(["debug"], strict: [debug: :string])

#おや...
{[], ["debug"], []}

#boolean形式以外の場合にはリストに2つ以上の要素が必要になる模様(キーが必要っぽい)
iex(6)> OptionParser.parse(["--debug", "B'z"], strict: [debug: :string])
{[debug: "B'z"], [], []}

integer

#integerで(ry
#errorになってしまう
OptionParser.parse(["--debug", 10], strict: [debug: :integer])
** (FunctionClauseError) no function clause matching in Integer.count_digits/2

#文字列のまま渡してあげる必要があるようで勝手にintegerに変換してくれる
iex(7)> OptionParser.parse(["--debug", "10"], strict: [debug: :integer])
{[debug: 10], [], []}

たくさん渡す

#B'zがうまく解析されず第2リストに行かれてしまう
iex(14)> OptionParser.parse(["--debug", "singer", "B'z"], strict: [debug: :boolean, singer: :string])
{[debug: true], ["singer", "B'z"], []}

#これだと通った
iex(15)> OptionParser.parse(["--debug", "--singer", "B'z"], strict: [debug: :boolean, singer: :string])
{[debug: true, singer: "B'z"], [], []}

他のオプションについて

aliases

ざっくりと機能を見てみると、割り当てのためのオプションパラメーターであることが分かる
aliasesって通称みたいな意味があるのでそのまんまですね

#aliasesを使うことで-dが--debugに変換されて解析されている
ex(15)> OptionParser.parse(["-d"], aliases: [d: :debug],  strict: [debug: :boolean])
{[debug: true], [], []}

文字列や数値で試してみたがうまく解析されなかった
個人的には「-h」を「--help」ってやつぐらいしか使い道が思いつかない(booleanのみ)
そもそも文字列やら数値は省略して渡さないのでいいのか...

switches

strictとは違いswitchesは解析した際にマッチしなかった引数の型を設定できるっぽい
strictの場合は解析できなかったものは無視して処理されるため解析できなかった引数は第2リストで返ってくる
stictは厳しめでswitchesは優しめ程度に覚えておけばいい気がする
現状ではあまり使い分けの方法は思いつかない。switchesにすると自由度高すぎるから注意ぐらいかな

OptionParser.parse(["--debug"], switches: [debug: :count])
{[debug: 1], [], []}