1. 程式人生 > >Go 每日一庫之 cobra

Go 每日一庫之 cobra

簡介

cobra是一個命令列程式庫,可以用來編寫命令列程式。同時,它也提供了一個腳手架,
用於生成基於 cobra 的應用程式框架。非常多知名的開源專案使用了 cobra 庫構建命令列,如Kubernetes、Hugo、etcd等等等等。
本文介紹 cobra 庫的基本使用和一些有趣的特性。

關於作者spf13,這裡多說兩句。spf13 開源不少專案,而且他的開源專案質量都比較高。
相信使用過 vim 的都知道spf13-vim,號稱 vim 終極配置。
可以一鍵配置,對於我這樣的懶人來說絕對是福音。他的viper是一個完整的配置解決方案。
完美支援 JSON/TOML/YAML/HCL/envfile/Java properties 配置檔案等格式,還有一些比較實用的特性,如配置熱更新、多查詢目錄、配置儲存等。
還有非常火的靜態網站生成器hugo也是他的作品。

快速使用

第三方庫都需要先安裝,後使用。下面命令安裝了cobra生成器程式和 cobra 庫:

$ go get github.com/spf13/cobra/cobra

如果出現了golang.org/x/text庫找不到之類的錯誤,需要手動從 GitHub 上下載該庫,再執行上面的安裝命令。我以前寫過一篇部落格搭建 Go 開發環境提到了這個方法。

我們實現一個簡單的命令列程式 git,當然這不是真的 git,只是模擬其命令列。最終還是通過os/exec庫呼叫外部程式執行真實的 git 命令,返回結果。
所以我們的系統上要安裝 git,且 git 在可執行路徑中。目前我們只新增一個子命令version

。目錄結構如下:

▾ get-started/
    ▾ cmd/
        helper.go
        root.go
        version.go
    main.go

root.go

package cmd

import (
  "errors"

  "github.com/spf13/cobra"
)

var rootCmd = &cobra.Command {
  Use: "git",
  Short: "Git is a distributed version control system.",
  Long: `Git is a free and open source distributed version control system
designed to handle everything from small to very large projects 
with speed and efficiency.`,
  Run: func(cmd *cobra.Command, args []string) {
    Error(cmd, args, errors.New("unrecognized command"))
  },
}

func Execute() {
  rootCmd.Execute()
}

version.go

package cmd

import (
  "fmt"
  "os"

  "github.com/spf13/cobra"
)

var versionCmd = &cobra.Command {
  Use: "version",
  Short: "version subcommand show git version info.",
  
  Run: func(cmd *cobra.Command, args []string) {
    output, err := ExecuteCommand("git", "version", args...)
    if err != nil {
      Error(cmd, args, err)
    }

    fmt.Fprint(os.Stdout, output)
  },
}

func init() {
  rootCmd.AddCommand(versionCmd)
}

main.go檔案中只是呼叫命令入口:

package main

import (
  "github.com/darjun/go-daily-lib/cobra/get-started/cmd"
)

func main() {
  cmd.Execute()
}

為了編碼方便,在helpers.go中封裝了呼叫外部程式和錯誤處理函式:

package cmd

import (
  "fmt"
  "os"
  "os/exec"

  "github.com/spf13/cobra"
)

func ExecuteCommand(name string, subname string, args ...string) (string, error) {
  args = append([]string{subname}, args...)

  cmd := exec.Command(name, args...)
  bytes, err := cmd.CombinedOutput()

  return string(bytes), err
}

func Error(cmd *cobra.Command, args []string, err error) {
  fmt.Fprintf(os.Stderr, "execute %s args:%v error:%v\n", cmd.Name(), args, err)
  os.Exit(1)
}

每個 cobra 程式都有一個根命令,可以給它新增任意多個子命令。我們在version.goinit函式中將子命令新增到根命令中。

編譯程式。注意,不能直接go run main.go,這已經不是單檔案程式了。如果強行要用,請使用go run .

$ go build -o main.exe

cobra 自動生成的幫助資訊,very cool:

$ ./main.exe -h
Git is a free and open source distributed version control system
designed to handle everything from small to very large projects
with speed and efficiency.

Usage:
  git [flags]
  git [command]

Available Commands:
  help        Help about any command
  version     version subcommand show git version info.

Flags:
  -h, --help   help for git

Use "git [command] --help" for more information about a command.

單個子命令的幫助資訊:

$ ./main.exe version -h
version subcommand show git version info.

Usage:
  git version [flags]

Flags:
  -h, --help   help for version

呼叫子命令:

$ ./main.exe version
git version 2.19.1.windows.1

未識別的子命令:

$ ./main.exe clone
Error: unknown command "clone" for "git"
Run 'git --help' for usage.

編譯時可以將main.exe改成git,用起來會更有感覺