docker run
のように,コマンドの引数に別のコマンドを引き渡して内部でいい感じに実行する系のやつ.
FlagSet.SetInterspersed
cmd := &cobra.Command{
// snip.
// 何らかのコマンドが渡されてることを保証する(not required だが,大抵は必要なはず)
Args: cobra.MinimumNArgs(1),
}
// ref: https://godoc.org/github.com/spf13/pflag#FlagSet.SetInterspersed
// `Flags()` or `PersistentFlags()` はユースケースによっていい感じに使い分ける
cmd.Flags().SetInterspersed(false)
cobra(pflags)はデフォルトだと引数とオプションがごちゃごちゃ(interspersed
)に渡ってきてもちゃんとパースされる.なのでこれを off にすることで,最初の flag じゃない引数より後ろは flag としてパースされなくなる.
実装は pflags の flag.go にあって,最初の引数が見つかった瞬間に flag のパースをやめるというもの.
日本語だと意味不明だけど,だいたい みたいな挙動になる.
$ # intespersed: true (default)
$ # args == []string{"run", "qux", "quux"}
$ fooctl --verbose run --bar=42 qux --baz quux
$ # intespersed: false
$ # args == []string{"run", "--bar=42", "qux", "--baz", "quux"}
$ fooctl --verbose run --bar=42 qux --baz quux
SetInterspersed(false)
でも,上の例だと run
より後ろ(--verbose
)は正しくパースされる.もちろん,未定義のフラグならエラーになる.
UnknownFlags?
cobra.Command
に FParseErrWhitelist.UnknownFlags
というものがあるが,これは単に未知のフラグを無視するようになるだけなので,今回は関係ない.
余談
docker run
は run
で SetInterspersed(false)
をしている.
https://github.com/docker/cli/blob/v18.09.0/cli/command/container/run.go#L52-L53
一方,ほぼ同一の interface を持つ kubectl run
は interspersed が true
のまま.
https://github.com/kubernetes/kubernetes/blob/v1.12.2/pkg/kubectl/cmd/run.go#L166-L193
pflgas は --
以後の引数はパースしないので,そうやってコマンドを渡す.
yarn run
とかとおなじ,わりと一般的な挙動ではある.
$ # args == []string{"curl", "http://example.com/ping"}
$ kubectl run ... --image buildpack-deps:18.04-curl -- curl http://example.com/ping
interspersed を切るかどうかは,その CLI がどんな UX を提供したいかによって判断すると良さそう.