post-thumb

fishshell の argprase で未定義の引数を無視

  • 所要時間 約 3 分
  • 12 Mar, 2022

fishshell で任意のコマンドのラッパーコマンドを作成するとき、普段は便利な argparse の挙動に困ることがあります。 この記事では、そんなときに頼りになる argparse のオプション -i を紹介します。

便利な argparse

argparse は、私が fishshell を使い始めた 2 つの理由のうち 1 つになります。

  • argparse による便利で簡単なフラグ機能
  • completion が豊富にも関わらず、fishshell の起動が迅速

argparse がどのくらい簡単にフラグ処理をかけるのか、少し確認しましょう。 以下の function を含む hoge.fish を編集します。

1
2
3
4
5
6
function hoge
    set options n/name= g/greeting=
    argparse $options -- $argv

    echo $_flag_g, $_flag_name
end

この hoge.fish を読み込み function hoge をフラグ付きで実行してみます。

> source hoge.fish
> hoge -n World --greeting Hello
Hello, World

フラグはショートフラグ、ロングフラグのどちらも定義済みなので、 ショート/ロングのフラグを逆にしても

> hoge --name World -g Hello
Hello, World

ショートフラグのみにしても

> hoge -n World -g Hello
Hello, World

ロングフラグのみにしても問題ありません。

> hoge --name World --greeting Hello
Hello, World

個人的には fish_trace を有効にしてデバッグする事が多いので、この作業を阻害しかねないフラグ定義を作成する関数 fish_opt は使いません。 このことは別の記事で書きたいと思います。

argparse 不便な一面

便利な argparse ですが、不便なときもあります。 私は、検証目的で様々な環境の vCenter を操作し、その際 govc という便利なコマンドを利用します。 接続先の vCenter を簡単に切り替えるために、fishshell でラッパー関数を作ることができれば、日々の作業が捗りそうです。

最初に作ってみた関数はこちら。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function call_govc
    set options d/domain=
    argparse $options -- $argv

    GOVC_URL=vcsa.$_flag_domain \
    GOVC_USERNAME=administrator@$_flag_domain \
    GOVC_PASSWORD=VMware1! \
    GOVC_INSECURE=true \
    govc $argv
end
> call_govc --domain test.dev vm.ip /datacenter/vm/VM-1
192.168.11.101

無事にうまく動きます。vm.ip は後続で指定した仮想マシンに振られている IP アドレスを取得するオプションで、/datacenter/vm/VM-1 は仮想マシンのインベントリパスになります。argparse では --domain test.dev のみがパースされ、残りの値は $argv に残るであろうという目論見が達成できました。

しかし、以下のコマンドを実行するとエラーが発生します。 find で vCenter のインベントリを検索、-type m で仮想マシンに対象を絞り、/datacenter 配下のインベントリを検索します。

1
2
3
4
5
6
> call_govc --domain test.dev find -type m /datacenter
call_govc: Unknown option '-type'
Usage of govc:
  about
  about.cert
  ...

2 行目のエラーは argparse のエラーで、2 行目以降のエラーは call_govc 内で呼び出している govc が正しい引数を渡されていないために起きるエラーになります。 argparse のエラーは govc のサブコマンドである find の引数 -type を、解析しようとして定義がなかったため起きています。 vm.ip がうまく実行できたのは、たまたま - を含むオプションがなかったためです。

この問題は、未定義のフラグについて argparse が無視して処理をしない、という動きをしてくれれば解決できます。 これを解決してくれるのが、次に説明する -i オプションとなります。

-i オプション

argparse-i,--ignore-unknown オプションをつければ期待した処理がなされます。

call_govcargparse-i オプションをつけます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function call_govc
    set options d/domain=
    argparse -i $options -- $argv

    GOVC_URL=vcsa.$_flag_domain \
    GOVC_USERNAME=administrator@$_flag_domain \
    GOVC_PASSWORD=VMware1! \
    GOVC_INSECURE=true \
    govc $argv
end

3 行目の argparse では、--domain フラグのみがパースされ、残りの find -type m /datacenter$argv に残ります。 この $argv が govc に渡されることで、govc のサブコマンドの引数に - (ハイフン) が含まれていても正常に govc が実行できるようになります。

1
2
> call_govc --domain test.dev find -type m /datacenter
/datacenter/vm/VM-1

-i オプションの制限

現状では known なショートフラグと unknown なショートフラグをまとめて与えてしまったときに、$argv のリストから known なショートフラグを外してくれません。 これは公式の argparse のページに記載があります。

> set -l argv -ho
> argparse -i h -- $argv
> echo $argv
-ho

この例では、ショートフラグ ho$argv に与えて、argparse では h のみをショートフラグとして処理しています。 このとき、期待としては echo $argv で、unknown なショートフラグである o のみとなって -o と出力されてほしいところですが、 known なショートフラグ h を含む -ho$argv に残ってしまいます。

また、最初の unknown なフラグまでしかパースしないともありますが、こちらは確認できませんでした。

Additionally, it can only parse known options up to the first unknown option in the group - the unknown option could take options, so it isn’t clear what any character after an unknown option means.

> set -l argv -l hoge -o fuga -a poyo
> argparse -i l= a= -- $argv
> echo $_flag_a
poyo
> echo $argv
-o fuga

unknown なフラグの後にある -a poyo をパースして $_flag_apoyo を代入しているし、$argv からは unknown なフラグの後にある -a poyo も取り除かれています。unknown なフラグ以降もパースできているようにみえます。

argparse.cpp を細かく追うまでの気力と力量がなく、調査はここまでとなります。ドキュメントを尊重し、known なフラグを前に配置するように心がけたいと思います。

comments powered by Disqus