要約
fish 2.7.0でargparseという非常に便利な関数が入ったので,オプション解析にはこれを使おう!
fishでのこれまでのオプション解析
fish shellの関数でオプション解析するには,これまでは以下のような方法がありました.
- OSが提供するgetoptコマンド (GNU実装 or BSD実装)
- fisherman/getopts
- oh-my-fish/plugin-getopts
- oh-my-fish/plugin-argu
- 自前で解析する
getoptについては以下の記事でも触れられていますが,GNU実装とBSD実装に互換性がない問題があります.
この問題はfishプラグインでも同様で,fisherman/getoptsとoh-my-fish/plugin-getoptsはともにgetopts
というコマンドを提供しますが,コマンド名が被っているのに使い方に互換性がありませんでした(つまり,作った関数を他人に渡すと上手く動かない可能性がある).
その点,oh-my-fish/plugin-arguは名前被りもなく,使い方も分かりやすくて機能も申し分なかったのですが,ちょっとしたバグがあり特定のオプション1が解析できませんでした.
つまりどの方法にも難点があり,オプション解析の決定的な方法はありませんでした.
しかし,先月(2017/11/27)リリースされたfish2.7.0でオプション解析用の関数argparse
がビルトインとして入りました.manを見ると「このコマンドはfishのスクリプトや関数が,fishのビルトイン関数が引数を扱うのと100%同じ方法で引数を取り扱うのを簡単にします.」とのこと.
『ビルトイン関数が引数を扱うのと100%同じ方法』で! いいですね.これからのオプション解析にはargparse
を使うのがよさそうです.
argparseの使い方
詳細な使い方については,fishにmanが付属しています.自分用にmanを日本語訳したものをGistにあげているので,よければそちらも見てみてください.
ここでは,具体的な例を示しながらargparse
の使い方を紹介します.
オプションを指定する
なにかすごいことをするコマンドsugoi
を定義するとします.とりあえず-v(--version)
でバージョン情報を表示するには,以下のようにします.
function sugoi
argparse -n sugoi 'v/version' -- $argv
or return 1
if set -lq _flag_version
echo "sugoi, version 0.0"
return
end
# ここで何かすごいことをする
end
argparse
は不正なオプションを受け取った際のエラーメッセージ表示も自動でやってくれるので,エラーメッセージに表示されるコマンド名を-n(--name)
オプションで指定します.
$ sugoi -z
sugoi: Unknown option '-z'
~/.config/fish/functions/sugoi.fish (line 3):
in function 'sugoi'
called on standard input
with parameter list '-z'
'v/version'
はオプションの仕様で,-v
および--version
が有効になります.sugoi
コマンドに-v
あるいは--version
オプションが指定されると_flag_v
および_flag_version
というローカル変数が定義されます2.ここではset -lq _flag_version
で変数が定義されたか確認してバージョン情報を表示しています3.
今は'v/version'
オプションだけですが,オプション仕様はもちろん複数指定できます.オプション仕様を列挙したあと,--
に続けて解析されるべき$argv
を置きます.
--
がオプション仕様と,解析される引数との区切りの役目をします.
argparse
は解析に失敗すると非ゼロの終了ステータスを返すので,argparse
文の後にor return 1
を続けて関数を終了します.
ロングオプションだけを使う
先程は-v
あるいは--version
でバージョン情報を表示しましたが,ロングオプションの--version
だけを使いたい場合はv/version
の代わりにv-version
とします.一応ショートオプションのv
も書いておく不思議な書式です.
引数を取るオプションを使う
-o(--output)
オプションで,sugoi
コマンドの出力を指定したファイルに出力できるようにします.オプションの仕様に=
を続けて'o/output='
とするとオプションが引数を取るものと扱われます.
function sugoi
argparse -n sugoi 'v/version' 'o/output=' -- $argv
or return 1
if set -lq _flag_version
echo "sugoi, version 0.0"
return
end
set -lq _flag_output
or set -l _flag_output '/dev/stdout' # デフォルトでは標準出力に吐く
begin
echo "何かすごいこと"
end > $_flag_output
end
以下のように動きます.
$ sugoi -o file1.txt
$ cat file1.txt
何かすごいこと
オプションの値を複数保持する
先程定義したsugoi
コマンドで,sugoi -o file1.txt -o file2.txt
のように複数回-o
オプションを使用すると最後に指定された値file2.txt
だけが_flag_output
にセットされます.
これはこれで適切な振る舞いな気がしますが,ここでは景気良く-o
で指定された全てのファイルに出力するようにしてみます.同じオプションで指定された値を全て保持するには,o/output=
のかわりにo/output=+
とします.
function sugoi
argparse -n sugoi 'v/version' 'o/output=+' -- $argv
or return 1
if set -lq _flag_version
echo "sugoi, version 0.0"
end
set -lq _flag_output
or set -l _flag_output '/dev/stdout' # デフォルトでは標準出力に吐く
for output in $_flag_output
begin
echo "何かすごいこと"
end > $output
end
end
同時に指定不可能なオプションを設定する
たとえば,出力をカンマ区切りにする-c
オプション,タブ区切りにする-t
オプション,空白区切りにする-w
オプションを作るとします.これらのオプションはどれか一つしか設定できないはずです.argparse
では-x(--exclusive)
オプションで,このようなオプション同士の排他的な関係を指定することができます.
function sugoi
argparse -n sugoi -x 'c,t,w' \
'v/version' 'o/output=+' 'c' 't' 'w' -- $argv
or return 1
# 以下省略
この状態で-c
と-t
を同時に指定するとエラーになります.
$ sugoi -ct
sugoi: Mutually exclusive flags 'c' and `t` seen
おわりに
argparse
では他にも色々なことができて,オプション引数が整数値でなければエラーにする,といったこともできるのですが,長くなるので詳しくはmanを御覧ください.
オプション解析までビルトイン関数でできる! fishは本当にuser-friendlyなシェルですね!