SoFunction
Updated on 2025-03-05

Go language Pflag Viper Cobra Cobra Core Functions Introduction

1.How to build an application framework

Generally speaking, building an application framework includes 3 parts:

  • Command line parameter analysis
  • Configuration file parsing
  • The command line framework for the application: it needs to have Help functions, be able to parse command line parameters and configuration files, and the command needs to be able to initialize business code and finally start the business process. The previous three requirements involve the use of Pflag, Viper, and Cobra, and these three packages are also connected to each other.

2. Command line parameter analysis tool: Pflag

Although a standard library Flag package is provided in the Go source code to parse command line parameters, another package that is more widely used in large projects is Pflag

2.1 Pflag package Flag definition

Pflag can process command line parameters. A command line parameter will be parsed into a variable of type Flag in the Pflag package.

type Flag struct {
    Name                string // Name of flag length option    Shorthand           string // The name of the flag short option, an abbreviation character    Usage               string // Use text of flag    Value               Value  // The value of flag    DefValue            string // default value of flag    Changed             bool // Record whether the flag value has been set    NoOptDefVal         string // The default value when flag appears on the command line but no option value is specified    Deprecated          string // Record whether the flag is abandoned    Hidden              bool // If the value is true, hide the flag from the help/usage output information    ShorthandDeprecated string // If the short option of flag is discarded, print the information when using the short option of flag    Annotations         map[string][]string // Set annotation for flag}

The value of Flag is an interface of type Value. Value is defined as follows

type Value interface {
    String() string // Convert the value of type flag to a value of type string and return the content of string    Set(string) error // Convert the value of type string to a value of type flag, and the conversion fails and reports an error    Type() string // Returns the type of flag, such as: string, int, ip, etc.}

2.2 Pflag package FlagSet definition

In addition to supporting a single Flag, Pflag also supports FlagSet. FlagSet is a collection of predefined flags

  • Call NewFlagSet to create a FlagSet
  • Global FlagSet defined using Pflag package: CommandLine
var version bool
flagSet := ("test", )
(&version, "version", true, "Print version information and quit.")
=================================
import (
    "/spf13/pflag"
)
(&version, "version", "v", true, "Print version information and quit.")
func BoolVarP(p *bool, name, shorthand string, value bool, usage string) {
    flag := (newBoolValue(value, p), name, shorthand, usage)
     = "true"
}

2.3 How to use Pflag

  • Supports multiple command line parameter definition methods
// Support long options, default values ​​and usage text, and store the value of the flag in a pointer.var name = ("name", "colin", "Input Your Name")
// Support long options, short options, default values ​​and usage text, and store the value of the flag in a pointervar name = ("name", "n", "colin", "Input Your Name")
// Support long options, default values ​​and usage text, and bind the value of the flag to a variable.var name string
(&name, "name", "colin", "Input Your Name")
// Support long options, short options, default values ​​and usage text, and bind the value of the flag to a variable.var name string
(&name, "name", "n","colin", "Input Your Name")
// The function name with Var indicates that the value of the flag is bound to a variable, otherwise the value of the flag is stored in a pointer// The function name with P indicates that short options are supported, otherwise short options are not supported.
  • Use Get to get the value of the parameter.
i, err := ("flagname")
  • Get non-optional parameters.
package main
import (
    "fmt"
    "/spf13/pflag"
)
var (
    flagvar = ("flagname", 1234, "help message for flagname")
)
// After defining the flag, you can call () to parse the defined flag.  After parsing, all non-option parameters can be returned by () and the i-th non-option parameters can be returned by (i).  Parameter subscripts 0 to () - 1.func main() {
    ()
    ("argument number is: %v\n", ())
    ("argument list is: %v\n", ())
    ("the first argument is: %v\n", (0))
}
  • The default value when an option is specified but no option value is specified.
var ip = ("flagname", "f", 1234, "help message")
("flagname").NoOptDefVal = "4321"
--flagname=1357 ==> 1357
--flagname ==> 4321
[nothing] ==> 1234
  • Abbreviation of deprecating signs or logos
// deprecate a flag by specifying its name and a usage message
("logmode", "please use --log-mode instead")
  • Keep the flag named port, but deprecate its abbreviation
(&port, "port", "P", 3306, "MySQL service host port.")
// deprecate a flag shorthand by specifying its flag name and a usage message
("port", "please use --port only")
  • Hide sign.
// Flag can be marked as hidden, meaning it will still function properly but will not be displayed in the usage/help text.// hide a flag by specifying its name
("secretFlag")

3. Configure parsing artifact: Viper

Almost all backend services require some configuration items to configure our services; Viper is a modern and complete solution for Go applications, able to handle configuration files in different formats. Viper can read configurations from different locations, and configurations at different locations have different priorities:

  • Display the settings via function
  • Command line parameters
  • Environment variables
  • Configuration File
  • Key/Value Storage
  • default value

3.1 Read-in configuration

Reading the configuration means reading the configuration into Viper, with the following read-in method:

  • Set the default value.
// Setting the default value is usually useful when key is not set through configuration files, environment variables, remote configurations, or command line flags("ContentDir", "content")
("LayoutDir", "layouts")
("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
  • Read configuration files
package main
import (
  "fmt"
  "/spf13/pflag"
  "/spf13/viper"
)
var (
  cfg  = ("config", "c", "", "Configuration file.")
  help = ("help", "h", false, "Show this help message.")
)
func main() {
  ()
  if *help {
    ()
    return
  }
  // Read configuration from configuration file  if *cfg != "" {
    (*cfg)   // Specify the configuration file name    ("yaml") // If there is no file extension in the configuration file name, you need to specify the format of the configuration file and tell viper to parse the file in which format  } else {
    (".")          // Add the current directory to the search path of the configuration file    ("$HOME/.iam") // Profile search path, you can set multiple profile search paths    ("config")     // Configuration file name (no file extension)  }
  if err := (); err != nil { // Read the configuration file.  If a configuration file name is specified, the specified configuration file is used, otherwise search in the registered search path    panic(("Fatal error config file: %s \n", err))
  }
  ("Used configuration file is: %s\n", ())
}
  • Listen and reread configuration files. Viper supports allowing applications to read configuration files in real time at runtime, that is, hot load configuration
()
(func(e ) {
   // Callback function that will be called after the configuration file is changed  ("Config file changed:", )
})
//It is not recommended to use the hot loading function in actual development, because even if the hot load is configured, the code in the program may not necessarily be hot loaded.  For example: The service listening port has been modified, but the service has not restarted. At this time, the service is still listening on the old port, which will cause inconsistency
  • Set configuration values
// You can explicitly set the configuration through the () function:("", "colin")
  • Use environment variables
// Use environment variables("VIPER_USER_SECRET_ID", "QLdywI2MrmDVjSSv6e95weNRvmteRjfKAuNV")
("VIPER_USER_SECRET_KEY", "bVix2WBv0VPfrDrvlLWrhEdzjLpPCNYb")
()                                             // Read environment variables("VIPER")                                      // Set the environment variable prefix: VIPER_, if it is viper, it will automatically convert to uppercase.((".", "_", "-", "_")) // Replace '.' and '-' in (key) key string with '_'("-key")
("-id", "USER_SECRET_ID") //Bind the environment variable name to the key
  • Viper supports Pflag packages using flags, and can bind keys to Flags. Similar to BindEnv, the value is not set when the binding method is called, but is set when accessing it.
("token", ("token")) // Bind a single flag()             //Binding flag set

3.2 Read configuration

  • Access nested keys.
{
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}
("") // (Return "127.0.0.1")
  • Deserialization Viper can support parsing all or specific values ​​to structures, maps, etc. It can be implemented through two functions:
type config struct {
  Port int
  Name string
  PathMap string `mapstructure:"path_map"`
}
var C config
err := (&C)
if err != nil {
  ("unable to decode into struct, %v", err)
}

Viper uses /mitchellh/maplanture to parse values ​​in the background, which uses mapstructure tags by default. When we need to desequence the configuration read by Viper into the structure variables we define, we must use mapstructure tags

  • Serialize to string
import (
    yaml "/yaml.v2"
    // ...
)
func yamlStringSettings() string {
    c := ()
    bs, err := (c)
    if err != nil {
        ("unable to marshal config to YAML: %v", err)
    }
    return string(bs)
}

4. Modern command line framework: Cobra

Cobra is both a library that can create powerful modern CLI applications and a program that can generate applications and command files. Applications usually follow the following pattern: APPNAME VERB NOUN --ADJECTIVE or APPNAME COMMAND ARG --FLAG VERB stands for verbs, NOUN stands for nouns, and ADJECTIVE stands for adjectives

4.1 Create commands using the Cobra library

  • Create rootCmd
$ mkdir -p newApp2 && cd newApp2
// Normally, we will put rootCmd in the file cmd/.var rootCmd = &{
  Use:   "hugo",
  Short: "Hugo is a very fast static site generator",
  Long: `A Fast and Flexible Static Site Generator built with
                love by spf13 and friends in Go.
                Complete documentation is available at http://hugo.`,
  Run: func(cmd *, args []string) {
    // Do Stuff Here
  },
}
func Execute() {
  if err := (); err != nil {
    (err)
    (1)
  }
}
// You can also define flags and process configurations in the init() functionimport (
  "fmt"
  "os"
  homedir "/mitchellh/go-homedir"
  "/spf13/cobra"
  "/spf13/viper"
)
var (
    cfgFile     string
    projectBase string
    userLicense string
)
func init() {
  (initConfig)
  ().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.)")
  ().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. /spf13/")
  ().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution")
  ().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)")
  ().Bool("viper", true, "Use Viper for configuration")
  ("author", ().Lookup("author"))
  ("projectbase", ().Lookup("projectbase"))
  ("useViper", ().Lookup("viper"))
  ("author", "NAME HERE <EMAIL ADDRESS>")
  ("license", "apache")
}
func initConfig() {
  // Don't forget to read config either from cfgFile or from home directory!
  if cfgFile != "" {
    // Use config file from the flag.
    (cfgFile)
  } else {
    // Find home directory.
    home, err := ()
    if err != nil {
      (err)
      (1)
    }
    // Search config in home directory with name ".cobra" (without extension).
    (home)
    (".cobra")
  }
  if err := (); err != nil {
    ("Can't read config:", err)
    (1)
  }
}
  • Creation We also need a main function to call rootCmd. Usually we will create a file and call () in it to execute the command
package main
import (
  "{pathToYourApp}/cmd"
)
func main() {
  ()
}
  • Add command Normally, we will put the source code files of other commands in the cmd/ directory. For example, we will add a version command to create a cmd/ file
package cmd
import (
  "fmt"
  "/spf13/cobra"
)
func init() {
  (versionCmd)
}
var versionCmd = &{
  Use:   "version",
  Short: "Print the version number of Hugo",
  Long:  `All software has versions. This is Hugo's`,
  Run: func(cmd *, args []string) {
    ("Hugo Static Site Generator v0.9 -- HEAD")
  },
}
  • Compile and run
$ go mod init /marmotedu/gopractise-demo/cobra/newApp2
$ go build -v .
$ ./newApp2 -h
A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at http://hugo.
Usage:
hugo [flags]
hugo [command]
Available Commands:
help Help about any command
version Print the version number of Hugo
Flags:
-a, --author string Author name for copyright attribution (default "YOUR NAME")
--config string config file (default is $HOME/.)
-h, --help help for hugo
-l, --license licensetext Name of license for the project (can provide licensetext in config)
-b, --projectbase string base project directory eg. /spf13/
--viper Use Viper for configuration (default true)
 Use "hugo [command] --help" for more information about a command.

4.2 Use flags

Cobra can be used in combination with Pflag to achieve powerful logo functions

// Use persistent flags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
// Assign a local flag, which can only be used on the commands it binds to.().StringVarP(&Source, "source", "s", "", "Source directory to read from")
// Bind the flag to Vipervar author string
func init() {
 ().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")
 ("author", ().Lookup("author"))
}
// Set the flag as required().StringVarP(&Region, "region", "r", "", "AWS region (required)")
("region")

5. Summary

When developing a Go project, we can parse command line parameters through Pflag, parse configuration files through Viper, and implement the command line framework using Cobra.

You can set command line parameters through the (), (), (), (), and () methods, and use Get to get the parameter value

The above is the detailed introduction to the core functions of go language Pflag Viper Cobra. For more information about Go language Pflag Viper Cobra, please follow my other related articles!