post-thumb

fish_opt を使わない

  • 所要時間 約 4 分
  • 14 Mar, 2022

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 でその変更を吸収できるかもしれないことくらいでしょうか。 トレースの苦しみを超えるメリットを見つけられずにいます。

comments powered by Disqus