電波ビーチ

☆(ゝω・)v

cobraで引数不要なフラグを受け取る

あらすじ及び結論

-hとか--listとか、そういうやつが欲しかった。ふつのコマンドでよく使うし標準で専用のメソッドなりがあるのかと思ったがそんなことはない?見つけたのは以下のやつ。ちょっと古い||もっといい方法があるのかもしれませんが

github.com

rootCmd.BoolVarP(&verbose, "verbose", "v", false, "verbose output")

このようにBoolVarPして成否判定やってみてけろ、ということらしい。たとえば今回は「viperで設定ファイルにある宛先メールアドレスをリストでほしい」という自作コマンドの要望上で欲しかった機能で、サブコマンドconfigを使って次のようにして表示したかったのです。ショートフラグ非対応なのは趣味です

<メインのコマンド> config --sendto

こうなった

サブコマンドのgoファイル。cobra add <サブコマンド名>で生成されたやつ

package cmd

import (
    "fmt"

    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var sendtoFlag bool

var configCmd = &cobra.Command{
    Use:   "config",
    Short: "設定ファイルでよく参照しそうな中身を表示",
    Long: `設定ファイルでよく参照しそうなものを表示します。
  
--sendto  送信先のメールアドレスいちらん
`,
    Run: func(cmd *cobra.Command, args []string) {
        mails, _ := cmd.Flags().GetBool("sendto")

        if mails && len(cmd.Flags().Args()) == 0 { // `--sendto`以外は不要
            for _, address := range viper.GetStringSlice("sendto") {
                fmt.Println(address)
            }
        } else {
            fmt.Printf("不正な引数もしくはオプションがあります。\n Args: %v\n", cmd.Flags().Args())
        }
    },
}

func init() {
    rootCmd.AddCommand(configCmd)
    configCmd.Flags().BoolVarP(&sendtoFlag, "sendto", "", false, "送信先のメールアドレス")

}

yamlはこんなのとする。同じ階層にあるのと名前は以下のroot.go参照。要らん設定たくさんあるけど省くのもめんどくさかったので載せる。記事内のsnippetでは当然sendtoしか使ってないです

applicationName: "ニャー"
debug: false
user: 
  name: "this is fuckin awesome name"
  age: 34882349
sendto: [aaa@mail.domain, bbb@mail2.gandum, ccc@koikeya.umai]

root.go載せる意味あんまなさそうだけどメモとして一部必要そうなところを置いておく。viperすらも初めて触ったんですがそもそもgoの文法とかよくわかってないので使いやすいのかどうか判断がつきましぇん

.
.
.
type Config struct {
    User            User     `yaml:user`
    ApplicationName string   `yaml:applicationName`
    Debug           bool     `yaml:debug`
    SendTo          []string `yaml:sendto`
}

type User struct {
    Name string `yaml:"name"`
    Age  int    `yaml:"age"`
}

var config Config

// 読み込む設定ファイル。定義してない(適当にネット上のソースをコピペしたなのでなんで定義されてないのか知らない)
var cfgFile string
.
.
.



func init() {
    cobra.OnInitialize(initConfig)
    // bra bra bra...
}

func initConfig() {
    if cfgFile != "" {
        // Use config file from the flag.
        viper.SetConfigFile(cfgFile)
    } else {
        viper.AddConfigPath(".")
        viper.SetConfigName("cfg")
        viper.SetConfigType("yaml") // もしかして明示的に書く必要ないかもしれない
    }

    if err := viper.ReadInConfig(); err != nil {
        fmt.Println("Can't read config:", err)
        os.Exit(1)
    }

    if err := viper.Unmarshal(&config); err != nil {
        fmt.Println("config file Unmarshal error")
        fmt.Println(err)
        os.Exit(1)
    }
}

試してみるよ

$ go run main.go config --sendto
aaa@mail.domain
bbb@mail2.gandum
ccc@koikeya.umai

$ go run main.go config --sendto 193838795237984
不正な引数もしくはオプションがあります。
 Args: [193838795237984]

$ go run main.go config sendto
不正な引数もしくはオプションがあります。
 Args: [sendto]

$ go run main.go config --send2 nya-
Error: unknown flag: --send2
Usage:
  cobra_practice config [flags]

Flags:
  -h, --help     help for config
      --sendto   送信先のメールアドレス

exit status 1

ちゃんと--sendtoとロングオプションのみつけたときだけ取れてますねぇ。CLIツールとかCLI用ライブラリとかのお作法って知らないんですがどのライブラリもこんな感じなのかな。