gocommand-line-interfacego-cobra

When making a CLI in Go using Cobra, how can I detect whether the user specified a bool flag?


I want to create a CLI using Cobra where the user does not have to provide each flag because the program interactively prompts them for values for flags they left unset. I have so far had no problem doing this for string flags. I use the method StringVar which lets me set the flag. It defaults to an empty string, so I can check the string variable's value and prompt the user for a value if it is empty. If it isn't empty, I can assume the user set a value and skip prompting them.

I have encountered a problem doing this for bool flags. I tried using the method BoolVar which is the bool equivalent of StringVar. The problem is that I need to pick a default value. Whether I pick false or true, the code in my program to check whether the user set a value cannot distinguish between the user setting false explicitly (if I choose false for the default value) or true explicitly (if I choose true for the default value).

This small example program reproduces my issue:

module example.com

go 1.23.2

require github.com/spf13/cobra v1.8.1

require (
    github.com/inconshreveable/mousetrap v1.1.0 // indirect
    github.com/spf13/pflag v1.0.5 // indirect
)

package main

import (
    "fmt"
    "os"
    "strings"

    "github.com/spf13/cobra"
)

var (
    rootCmd = &cobra.Command{
        Run: func(cmd *cobra.Command, args []string) {
            message := "hello world"
            if upper {
                message = strings.ToUpper(message)
            }
            fmt.Println(message)
        },
    }
    upper bool
)

func init() {
    rootCmd.Flags().BoolVar(&upper, "upper", false,
        "Whether to uppercase the message before printing it.")
}

func main() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

I can run it like this:

> go run main.go
hello world

Or like this:

> go run main.go --upper
HELLO WORLD

I cannot find a way to code it so that I can detect whether the user provided the flag so that I can prompt them for whether they want the message to be modified before being printed.

I have thought of a workaround being that I can use a string flag instead of bool flag (perhaps having "yes" and "no" being the only allowed values) and documenting this for users so they know it doesn't work like typical bool flags would work. Another work around I thought of was documenting to users that unlike other flags, this flag would have no interactive option, and that they must provide the flag as they launch the CLI. I would mark the flag as required.

Both of these workarounds would prevent me from achieving the user experience I was aiming for though, where users can choose between providing configuration 100% interactively, 100% using flags, or any combination of those.


Solution

  • Lookup the flag and see if it is changed:

    if !cmd.Flags().Changed("boolFlag) {
      // Flag was not set
    }