使用Cobra很簡單。首先,使用go get安裝最新版本
go get github.com/spf13/cobra/cobra
由於各種問題,國內使用 go get 安裝 golang 官方包可能會失敗。如果出現:
hidden@hidden:~$ go get github.com/spf13/cobra/cobra
package golang.org/x/sys/unix: unrecognized import path "golang.org/x/sys/unix" (https fetch: Get https:
package golang.org/x/text/transform: unrecognized import path "golang.org/x/text/transform" (https fetch: Get https:
package golang.org/x/text/unicode/norm: unrecognized import path "golang.org/x/text/unicode/norm" (https fetch: Get https:
即使使用代理也不行,怎麼解決這個問題?
其實 golang 在 github 上建立了一個鏡像庫,如 https://github.com/golang/sys/, 即是 https://golang.org/x/sys/ 的鏡像庫。要避免以上報錯,其實只需要執行以下步驟:
mkdir -p $GOPATH/src/golang.org/x
cd $GOPATH/src/golang.org/x
git clone https:
git clone https:
然後再執行go get github.com/spf13/cobra/cobra即可。
安裝成功之後會生成一個名為cobra的可執行文件:
hidden@hidden:~$ ls -al $GOPATH/bin | grep cobra
-rwxr-xr-x 1 zhuzhonghua zhuzhonghua 11411056 Aug 12 15:48 cobra
假設現在我們要開發一個基於CLI的命令程序,名字為demo。執行如下命令:
hidden@hidden:~$ cd $GOPATH/src/github.com/spf13/
hidden@hidden:~/go/src/github.com/spf13$ cobra init demo --pkg-name=github.com/spf13/demo
Your Cobra applicaton is ready at
/home/zhuzhonghua/go/src/github.com/spf13/demo
hidden@hidden:~/go/src/github.com/spf13$ cd demo/
在$GOPATH/src/github.com/spf13目錄下會生成一個demo的文件夾,結構如下:
hidden@hidden:~/go/src/github.com/spf13/demo$ tree
.
├── cmd
│ └── root.go
├── LICENSE
└── main.go
1 directory, 3 files
在demo目錄下會有如下的內容:
hidden@hidden:~/cobra/demo$ tree
.
├── cmd
│ └── root.go
├── LICENSE
└── main.go
1 directory, 3 files
測試cobra效果:
hidden@hidden:~/cobra/demo$ go run main.go
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
subcommand is required
exit status 1
在Cobra應用程式中,通常main.go文件非常空洞。它主要只幹一件事:初始化Cobra。
package main
import "github.com/spf13/demo/cmd"
func main() {
cmd.Execute()
}
可以定義其他命令,並且通常在cmd/目錄中為每個命令提供自己的文件。
如果要創建版本(version)命令,可以創建cmd/version.go並使用以下內容填充它:
hidden@hidden:~/go/src/github.com/spf13/demo$ cobra add version
version created at /home/zhuzhonghua/go/src/github.com/spf13/demo
此目錄結構變更為:
hidden@hidden:~/go/src/github.com/spf13/demo$ tree
.
├── cmd
│ ├── root.go
│ └── version.go
├── LICENSE
└── main.go
1 directory, 4 files
現在我們來執行以下這個子命令:
hidden@hidden:~/go/src/github.com/spf13/demo$ go run main.go version
version called
生成的version代碼如下(cmd/version.go):
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("version called")
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
註:命令的名稱使用駝峰式命名(camelCase),而不能使用蛇形命名(snake_case)。讀者可以自行創建一個蛇形命名的命令來查看一下實際的效果(hint: camelCase to snake_case)。
完善子命令功能修改上面代碼中的函數Run:
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("version called")
},
修改為:
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Version 1.0.0 for demo")
},
再次執行go run main.go version,結果如下:
Version 1.0.0 for demo
在上面的cmd/version.go中我們發現在init函數有這麼一行代碼:rootCmd.AddCommand(versionCmd),這個rootCmd是什麼呢?
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
var cfgFile string
var rootCmd = &cobra.Command{
Use: "demo",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.demo.yaml)")
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
viper.AddConfigPath(home)
viper.SetConfigName(".demo")
}
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
打開cmd/root.go(上面的示例代碼),你會發現rootCmd其實就是我們的根命令。我相信機智的同學已經猜出來我們添加子命令的子命令的方法了。現在讓我們在cmd目錄下新建help.go文件,項目文件結構為:
hidden@hidden:~/go/src/github.com/spf13/demo$ tree
.
├── cmd
│ ├── help.go
│ ├── root.go
│ └── version.go
├── LICENSE
└── main.go
1 directory, 5 files
其中cmd/help.go的內容為(help為version的子命令):
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var helpCmd = &cobra.Command{
Use: "help",
Short: "show command info",
Long: `<snip>`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Here is the help information")
},
}
func init() {
versionCmd.AddCommand(helpCmd)
}
執行go run main.go version help命令之後,結果為:
hidden:demo hidden$ go run main.go version help
Here is the help information
Flags提供了修飾符來控制動作命令的操作。
Persistent Flags:全局性flag, 可用於它所分配的命令以及該命令下的每個命令。在根上分配標誌作為全局flag。
Local Flags:局部性flag,在本args分配一個標誌,該標誌僅適用於該特定命令。
Required flags:必選flag,flag默認是可選的。如果希望命令在未設置flag時報告錯誤,請將其標記為required。
添加Flags如果仔細看過上面cmd/version.go中init函數中的注釋的話,你應該已經得到了足夠多的信息來自己操作添加flag。
不過這裡我再解釋一下,首先是persistent參數,當你的參數作為persistent flag存在時,如注釋所言,在其所有的子命令之下該參數都是可見的。而local flag則只能在該命令調用時執行。
可以做一個簡單的測試,在cmd/version.go的init函數中,添加如下內容(添加在rootCmd.AddCommand(versionCmd)這一行之上):
versionCmd.PersistentFlags().String("global_foo", "global_val", "A help for global_foo")
versionCmd.Flags().String("local_foo","local_val", "A help for local_foo")
現在運行go run main.go version -h得到如下結果:
hidden:demo hidden$ go run main.go version -h
A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Usage:
demo version [flags]
demo version [command]
Available Commands:
help show command info
Flags:
--global_foo string A help for global_foo (default "global_val")
-h, --help help for version
--local_foo string A help for local_foo (default "local_val")
Global Flags:
--config string config file (default is $HOME/.demo.yaml)
Use "demo version [command] --help" for more information about a command.
接著我們對比著再運行go run main.go help -h試試:
hidden:demo hidden$ go run main.go version help -h
<snip>
Usage:
demo version help [flags]
Flags:
-h, --help help for help
Global Flags:
--config string config file (default is $HOME/.demo.yaml)
--global_foo string A help for global_foo (default "global_val")
可以發現在Gloabal Flags的變化。version作為root的子命令,仍然可以使用root的persistent flag-> config(可以查看root.go),而help作為test的子命令,不僅可以使用test的persistent flag-> fool, 也可以使用test父命令的persistent flag。從而我們可以直觀的看出persistent的作用範圍是該命令之後的所有子命令。
flag支持的參數類型可以參考文檔:https://godoc.org/github.com/spf13/cobra
注意:cmd.Flags().String()與 cmd.Flags().StringP()是不一樣的。假如我們在version.go的init下增加如下兩行:
versionCmd.Flags().String("flag1","", "flag1 usage")
versionCmd.Flags().StringP("flga2","f","", "flag2 usage")
前者調用需要如下形式:
go run main.go version --flag1
後者有如下兩種形式調用:
go run main.go version --flag2
go run main.go version -f
其它示例:
persistent flag:
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
local flag:
rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
rootCmd.MarkFlagRequired("source") // Flags默認是可選的。如果您希望命令在未設置Flags時報告錯誤,請將其標記為必需
獲取Flags值在知道了如何設置參數後,我們的下一步當然是需要在運行時獲取改參數的值。現在我們把注意力放到version.go的這個部分:
var versionCmd = &cobra.Command{
Use: "version",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Version 1.0.0 for demo")
},
}
讓我們把注意力重新放到上面的代碼上。我們也很容易可以猜測到Use,Short,Long三個參數的作用,這裡便不做闡述。
顯而易見,我們應該在Run這裡來獲取參數並執行我們的命令功能。獲取參數其實也並不複雜。以versionCmd.Flags().StringP("flag2", "f", "", "flag2 usage")此為例,我們可以在Run函數裡添加:
str,_ := cmd.Flags().GetString("flag2")
fmt.Printf("The param vale is %s\n", str)
運行命令go run main.go version -f vvvv,獲得結果如下:
The param vale is vvvv
Version 1.0.0 for demo
可以使用Args欄位來指定位置參數的驗證Command。
以下驗證器內置:
NoArgs - 如果存在任何位置參數,該命令將報告錯誤。
ArbitraryArgs - 該命令將接受任何args。
OnlyValidArgs- 如果存在任何不在ValidArgs欄位中的位置參數,該命令將報告錯誤Command。
MinimumNArgs(int) - 如果沒有至少N個位置參數,該命令將報告錯誤。
MaximumNArgs(int) - 如果有多於N個位置參數,該命令將報告錯誤。
ExactArgs(int) - 如果沒有確切的N位置參數,該命令將報告錯誤。
RangeArgs(min, max) - 如果args的數量不在預期args的最小和最大數量之間,則該命令將報告錯誤。
為了演示Args的用法,我們在cmd目錄下再創建一個args.go文件,其內容如下(注意多了一個Args函數):
mport (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var argsCmd = &cobra.Command{
Use: "args",
Short: "args demo",
Long: `<snip>`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args)<1{
return errors.New("requires at least one arg")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("args called, args: ", args)
},
}
func init() {
rootCmd.AddCommand(argsCmd)
}
示例中限定參數的格式至少為一個否則會報錯。我們來運行一下看一看結果如何,首先是不添加參數(go run main.go args):
hidden:demo hidden$ go run main.go args
Error: requires at least one arg
Usage:
demo args [flags]
Flags:
-h, --help help for args
Global Flags:
--config string config file (default is $HOME/.demo.yaml)
requires at least one arg
exit status 1
可以看到報錯:Error: requires at least one arg。
我們再來試一下添加參數的結果:
hidden:demo hidden$ go run main.go args 1
args called, args: [1]
hidden:demo hidden$ go run main.go args 1 2 3 4
args called, args: [1 2 3 4]
示例中的Args函數可以替換為
Args: cobra.MinimumNArgs(1),
讀者可以自行驗證一下效果。
Help命令前面的示例中出現了cmd/help.go,為了不產生迷惑,我們把這個文件先刪除掉。
當您有子命令時,Cobra會自動為您的應用程式添加一個幫助命令。當用戶運行「app help」時會調用此方法。此外,幫助還將支持所有其他命令作為輸入。比如說,你有一個名為'create'的命令,沒有任何額外的配置; 當'app help create'被調用時,Cobra會工作。每個命令都會自動添加' - help'標誌。
可以在終端輸入cobra或者cobra help命令看一下實際的效果:
hidden@hidden:~$ cobra help
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Usage:
cobra [command]
Available Commands:
add Add a command to a Cobra Application
help Help about any command
init Initialize a Cobra Application
Flags:
-a,
-h,
-l,
Use "cobra [command] --help" for more information about a command.
可以提供自定義的help命令或自定義的模板,通過以下函數實現:
cmd.SetHelpCommand(cmd *Command)
cmd.SetHelpFunc(f func(*Command, []string))
cmd.SetHelpTemplate(s string)
後兩者也適用於子命令。
Usage當用戶提供無效標誌或無效命令時,Cobra會通過向用戶顯示「usage」來做出響應。
示例:
hidden@hidden:~$ cobra --invalid
Error: unknown flag: --invalid
Usage:
cobra [command]
Available Commands:
add Add a command to a Cobra Application
help Help about any command
init Initialize a Cobra Application
Flags:
-a, --author string author name for copyright attribution (default "YOUR NAME")
--config string config file (default is $HOME/.cobra.yaml)
-h, --help help for cobra
-l, --license string name of license for the project
--viper use Viper for configuration (default true)
Use "cobra [command] --help" for more information about a command.
您可以提供自己的使用功能或模板供Cobra使用。與Help一樣,函數和模板可以通過公共方法覆蓋:
cmd.SetUsageFunc(f func(*Command) error)
cmd.SetUsageTemplate(s string)
我們可以在Run方法之前或者之後運行一些其它的方法(函數)。PersistentPreRun和PreRun在Run之前執行。PersistentPostRun和PostRun將Run之後執行。Persistent***Run如果子程序沒有聲明他們自己的功能,他們將繼承這些功能。這些功能按以下順序運行:
PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
下面是使用所有這些功能的兩個命令的示例。執行子命令時,它將運行root命令的PersistentPreRun,但不運行root命令的PersistentPostRun:
import (
"fmt"
"github.com/spf13/cobra"
)
func main(){
var rootCmd = &cobra.Command{
Use: "root",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[root] PersistentPreRun with args: %v \n", args)
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[root] PreRun with args: %v \n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("[root] Run with args: %v \n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[root] PostRun with args: %v \n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[root] PersistentPostRun with args: %v \n", args)
},
}
var subCmd = &cobra.Command{
Use: "sub",
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[sub] PreRun with args: %v \n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("[sub] Run with args: %v \n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[sub] PostRun with args: %v \n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("[sub] PersistentPostRun with args: %v \n", args)
},
}
rootCmd.AddCommand(subCmd)
rootCmd.SetArgs([]string{""})
rootCmd.Execute()
fmt.Println()
rootCmd.SetArgs([]string{"sub", "arg1", "arg2",})
rootCmd.Execute()
}
運行結果:
[root] PersistentPreRun with args: []
[root] PreRun with args: []
[root] Run with args: []
[root] PostRun with args: []
[root] PersistentPostRun with args: []
[root] PersistentPreRun with args: [arg1 arg2]
[sub] PreRun with args: [arg1 arg2]
[sub] Run with args: [arg1 arg2]
[sub] PostRun with args: [arg1 arg2]
[sub] PersistentPostRun with args: [arg1 arg2]