fishshell でフラグのパースを行う際、fishshell で用意されている fish_opt
は利用しないようにしています。
冗長な記述となってしまうのに加え、デバッグ時のトレース出力が煩雑になりすぎることがその理由として挙げられます。
この記事では、fish_opt
を使わなくなった経緯について説明します。
標準で用意されている fish_opt
fishshell でフラグのパースを行うには、組み込み関数の argparse
を使います。
fish_opt
は、この argparse
にわたすフラグの定義をわかりやすく生成してくれます。
fish_opt
なしでは、以下のようなコードとなります。
1
2
|
set -l options n/name=
argparse $options -- $argv
|
fish_opt
ありでは、以下のような意味を汲み取りやすいコードとなります。
1
2
|
set -l options (fish_opt --short n --long name --required-val)
argparse $options -- $argv
|
3 つもフラグが並べばだるさが…
しかしながら、フラグが 3 つも並ぶとげんなりしてきます。
1
2
3
4
|
set -l options (fish_opt --short n --long name --required-val)
set -l options $options (fish_opt --short a --long address --required-val)
set -l options $options (fish_opt --short m --long mobile --required-val)
argparse $options -- $argv
|
流石に長い…。
フラグを持つ function やスクリプトを頻繁に書くようであれば、argparse
にわたすフラグの定義を書き慣れてきます。
1
2
|
set -l options n/name= a/address= m/mobile=
argparse $options -- $argv
|
と書いてしまったほうが遥かに簡潔となります。
デバッグを阻害する大量のトレース出力
fish_opt
を使って一番げんなりしたのは、デバッグのために fish_trace
を有効にしたときです。
1
2
3
4
5
6
|
#!/usr/bin/fish
set fish_trace true
set -l options (fish_opt --short n --long name --required-val)
set -l options $options (fish_opt --short a --long address --required-val)
set -l options $options (fish_opt --short m --long mobile --required-val)
argparse $options -- $argv
|
set fish_trace true
としてトレースを有効に設定し、これを hoge.fish
と保存し実行してみるとエグい出力が表示されます。
これが function の呼び出しのたびに出力されるのでは、トレースを追いづらくて仕方ありません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
> fish hoge.fish
--> source /usr/share/fish/functions/fish_opt.fish
----> function __fish_opt_validate_args --no-scope-shadowing
----> function fish_opt -d 'Produce an option specification suitable for use with `argparse`.'
-> fish_opt --short n --long name --required-val
---> set -l options h/help s/short= l/long= o/optional-val r/required-val
---> set options h/help s/short= l/long= o/optional-val r/required-val L-long-only M-multiple-vals
---> argparse -n fish_opt --max-args=0 --exclusive=r,o --exclusive=M,o h/help s/short= l/long= o/optional-val r/required-val L-long-only M-multiple-vals -- --short n --long name --required-val
---> if
---> set -q _flag_help
---> end if
---> __fish_opt_validate_args
-----> if
-----> set -q _flag_short
------> string length -- n
-----> test 1 -ne 1
-----> end if
-----> return 0
---> set -l opt_spec n
---> if
---> set -q _flag_long
----> if
----> set -q _flag_long_only
----> else
-----> set opt_spec n/
----> end if
----> set opt_spec n/name
---> end if
---> if
---> set -q _flag_multiple_vals
---> else if
---> set -q _flag_required_val
----> set opt_spec n/name=
---> end if
---> echo n/name=
> set -l options n/name=
-> fish_opt --short a --long address --required-val
---> set -l options h/help s/short= l/long= o/optional-val r/required-val
---> set options h/help s/short= l/long= o/optional-val r/required-val L-long-only M-multiple-vals
---> argparse -n fish_opt --max-args=0 --exclusive=r,o --exclusive=M,o h/help s/short= l/long= o/optional-val r/required-val L-long-only M-multiple-vals -- --short a --long address --required-val
---> if
---> set -q _flag_help
---> end if
---> __fish_opt_validate_args
-----> if
-----> set -q _flag_short
------> string length -- a
-----> test 1 -ne 1
-----> end if
-----> return 0
---> set -l opt_spec a
---> if
---> set -q _flag_long
----> if
----> set -q _flag_long_only
----> else
-----> set opt_spec a/
----> end if
----> set opt_spec a/address
---> end if
---> if
---> set -q _flag_multiple_vals
---> else if
---> set -q _flag_required_val
----> set opt_spec a/address=
---> end if
---> echo a/address=
> set -l options n/name= a/address=
-> fish_opt --short m --long mobile --required-val
---> set -l options h/help s/short= l/long= o/optional-val r/required-val
---> set options h/help s/short= l/long= o/optional-val r/required-val L-long-only M-multiple-vals
---> argparse -n fish_opt --max-args=0 --exclusive=r,o --exclusive=M,o h/help s/short= l/long= o/optional-val r/required-val L-long-only M-multiple-vals -- --short m --long mobile --required-val
---> if
---> set -q _flag_help
---> end if
---> __fish_opt_validate_args
-----> if
-----> set -q _flag_short
------> string length -- m
-----> test 1 -ne 1
-----> end if
-----> return 0
---> set -l opt_spec m
---> if
---> set -q _flag_long
----> if
----> set -q _flag_long_only
----> else
-----> set opt_spec m/
----> end if
----> set opt_spec m/mobile
---> end if
---> if
---> set -q _flag_multiple_vals
---> else if
---> set -q _flag_required_val
----> set opt_spec m/mobile=
---> end if
---> echo m/mobile=
> set -l options n/name= a/address= m/mobile=
> argparse n/name= a/address= m/mobile= --
|
さらに、最後の 2 行が fish_opt
を使わない場合の記述とおなじになってしまうため、げんなり度が更に上がります。
- フラグ定義の記述が冗長化する
- トレース出力が煩雑となる
以上 2 つの理由により fish_opt
を使わず、フラグ定義を直書きする対応で日々過ごしています。
fish_opt
が有用といえるのは、今後 fishshell のフラグ定義が変更になったときに fish_opt
でその変更を吸収できるかもしれないことくらいでしょうか。
トレースの苦しみを超えるメリットを見つけられずにいます。