Hello,
I'm trying to override the default help flag, but for some reason, it doesn't work. Here's the code:
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
app := &cobra.Command{
Use: "testing",
Short: "testing app",
PersistentPreRun: func(c *cobra.Command, args []string) {
c.Flags().BoolP("help", "h", false, "helping myself with the command "+c.Name())
},
}
var name string
hello := &cobra.Command{
Use: "hello",
Short: "example hello command",
Run: func(c *cobra.Command, args []string) {
if name == "" {
name = "World"
}
fmt.Printf("Hello, %s! \n", name)
},
}
hello.Flags().StringVar(&name, "name", "", "name to greet")
app.AddCommand(hello)
if err := app.Execute(); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
When ran, I see:
$ testing hello -h
example hello command
Usage:
testing hello [flags]
Flags:
-h, --help help for hello
--name string name to greet
So the PersistentPreRun is supposed to run before each Execute() command. Then the InitHelpFlag is supposed to check if there's already a flag named help, if so, never touch it.
Am I missing something?
You should declare all flags in init as it said in README.
This works for me:
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var app = &cobra.Command{
Use: "testing",
Short: "testing app",
}
func init() {
app.Flags().BoolP("help", "h", false, "helping myself with the command "+app.Name())
}
func main() {
var name string
hello := &cobra.Command{
Use: "hello",
Short: "example hello command",
Run: func(c *cobra.Command, args []string) {
if name == "" {
name = "World"
}
fmt.Printf("Hello, %s! \n", name)
},
}
hello.Flags().StringVar(&name, "name", "", "name to greet")
app.AddCommand(hello)
if err := app.Execute(); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
I would agree if I would be only modifying the help flag for the main function. But if I have a hundred subcommands under testing then it becomes a nightmare, hence me using the PersistentPreRun.
Is there any other way to achieve overriding the whole tree of help flags that's automatically generated?
I've been checking more and it seems you can modify the help _command_ entirely, as well as keeping the features of name interpolation and everything else, but not the Help tag if you have a big list of commands, unless you go one by one and replace them all.
What about modifying InitDefaultHelpFlag() to accept two string parameters, one for the help for string, and the second one for the hardcoded this command?
Anyhow, I ended up doing something easier to avoid having to go one by one. I modified the usage template, and where the .LocalFlags.FlagUsages gets printed and the trimRightSpace function gets called, I wrote a replacer that will find the line containing the help flag, which is usually -h, --help help for abc and then replace help for, returning the new string. I'm using regexs to find the proper line.
If it's not important for you to have a name in the description of your help flag, there is an elegant way to do it with PersistentFlags:
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var app = &cobra.Command{
Use: "testing",
Short: "testing app",
Run: func(c *cobra.Command, args []string) {},
}
func init() {
app.PersistentFlags().BoolP("help", "h", false, "helping myself with the command")
}
func main() {
var name string
hello := &cobra.Command{
Use: "hello",
Short: "example hello command",
Run: func(c *cobra.Command, args []string) {
if name == "" {
name = "World"
}
fmt.Printf("Hello, %s! \n", name)
},
}
hello.Flags().StringVar(&name, "name", "", "name to greet")
app.AddCommand(hello)
if err := app.Execute(); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
It may work, but there are 2 different help flag descriptions:
"help for " + c.Name()"help for this command"I don't know where the second one gets called (I haven't figured it out yet, I know it works on places where there's no command name) but the first one is definitely useful with the name, even more for descriptions or markdown docs.
I don't know where the second one gets called (I haven't figured it out yet, I know it works on places where there's no command name)
You're right, and it's only used there.
InitDefaultHelpFlag defines the help flag:
// InitDefaultHelpFlag adds default help flag to c.
// It is called automatically by executing the c or by calling help and usage.
// If c already has help flag, it will do nothing.
func (c *Command) InitDefaultHelpFlag() {
c.mergePersistentFlags()
if c.Flags().Lookup("help") == nil {
usage := "help for "
if c.Name() == "" {
usage += "this command"
} else {
usage += c.Name()
}
c.Flags().BoolP("help", "h", false, usage)
}
}
Agreed. So like I mentioned before, what if that InitDefaultHelpFlag allows you to pass your own set of strings for the usage part? That'll make it: a) localizable; and b) easily customizable
The only problem is that this change is not backward compatible
Then keep the function but allow the caller to change the hardcoded strings "help for " and "this command".
I found a solution:
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var app = &cobra.Command{
Use: "testing",
Short: "testing app",
Run: func(c *cobra.Command, args []string) {},
}
func main() {
var name string
hello := &cobra.Command{
Use: "hello",
Short: "example hello command",
Run: func(c *cobra.Command, args []string) {
if name == "" {
name = "World"
}
fmt.Printf("Hello, %s! \n", name)
},
}
hello.Flags().StringVar(&name, "name", "", "name to greet")
app.AddCommand(hello)
walk(app, func(c *cobra.Command) {
c.Flags().BoolP("help", "h", false, "helping myself with the command "+c.Name())
})
if err := app.Execute(); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
// walk calls f for c and all of its children.
func walk(c *cobra.Command, f func(*cobra.Command)) {
f(c)
for _, c := range c.Commands() {
walk(c, f)
}
}
If you have some questions, don't hesitate to ask.
Can you help me with one thing. I'm trying to search about flags and I'm work with cobra. My object is ./my-binary -ip=127.0.0.1 -mqtt-port=1883 and then I can change the values in my program
my main.go
GNU nano 2.9.3 main.go
package main
import (
"os"
"github.com/spf13/cobra"
)
func main() {
cmd := &cobra.Command{
Use: "gateway",
Short: "Bem-vindo",
SilenceUsage: true,
}
cmd.AddCommand(printTimeCmd())
cmd.AddCommand(printHelloCmd())
cmd.AddCommand(mainCmd())
cmd.AddCommand(jardimCmd())
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
print_cmd.go
func mainCmd() *cobra.Command {
return &cobra.Command{
Use: "sensor",
RunE: func(cmd *cobra.Command, args []string) error {
**ip := "tcp://127.0.0.1:1883"** -> I want to change this with command line with go
cmd.Println("O IP da gateway e o port s茫o:",ip)
//Connect to the broker
opts := MQTT.NewClientOptions()
opts.AddBroker(ip)
opts.SetUsername("your-username")
opts.SetPassword("your-password")
client := MQTT.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
fmt.Println("Connection error")
fmt.Println(token.Error())
}
$ cat main.go
package main
import (
"os"
"github.com/spf13/cobra"
)
const (
ipFlag = "ip"
)
func main() {
cmd := &cobra.Command{
Use: "gateway",
Short: "Bem-vindo",
SilenceUsage: true,
}
cmd.AddCommand(mainCmd())
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
func mainCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "sensor",
RunE: func(cmd *cobra.Command, args []string) error {
ip, err := cmd.Flags().GetString(ipFlag)
if err != nil {
return err
}
cmd.Println("O IP da gateway e o port s茫o:", ip)
return nil
},
}
cmd.Flags().String(ipFlag, "tcp://127.0.0.1:1883", "How to connect")
return cmd
}
$ go run main.go sensor
O IP da gateway e o port s茫o: tcp://127.0.0.1:1883
$ go run main.go sensor --ip=https://bob.com/
O IP da gateway e o port s茫o: https://bob.com/