golang常見庫cobra

cobra 是 go 語言的一個庫,能夠用於編寫命令行工具。一般咱們能夠看到git pulldocker container start apt install 等等這樣命令,均可以很容易用corba來實現,另外,go 語言是很容易編譯成一個二進制文件,本文將實現一個簡單的命令行工具。java

具體寫一個例子, 設計一個命令叫blog, 有四個子命令linux

blog new [post-name] :建立一篇新的blog
 blog list   :列出當前有哪些文章
 blog delete [post-name]: 刪除某一篇文章
 blog edit [post-name]:編輯某一篇文章

計劃有如下幾個步驟git

  • 建立模塊
  • 用cobra的命令行,建立命令行入口
  • 用cobra的命令行,建立子命令
  • 編寫功能邏輯

建立模塊

$ go mod init github.com/shalk/blog
go: creating new go.mod: module github.com/shalk/blog

建立命令行入口

說到命令行,可能會想到bash的getopt 或者 java 的jcommand,能夠解析各類風格的命令行,可是一般這些命令行都有固定的寫法,這個寫法通常還記不住要找一個模板參考如下。cobra除了能夠解析命令行以外,還提供了命令行,能夠生成模板。先安裝這個命令行, 而且把庫的依賴加到go.mod裏github

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

cobra會安裝到$GOPATH\bin 目錄下,注意環境變量中把它加入PATH中golang

$ cobra init --pkg-name github.com/shalk/blog -a shalk -l mit
Your Cobra applicaton is ready at
D:\code\github.com\shalk\blog

目錄結構以下:docker

./cmd
./cmd/root.go
./go.mod
./go.sum
./LICENSE
./main.go

編譯一下vim

go build  -o blog .

執行一下bash

$blog -h
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.

命令行就建立好了,看上去一行代碼也不用寫,因爲隨着瞭解的深刻,後面須要調整生成的代碼,仍是要了解一下cobra代碼的套路。app

cobra代碼的套路

有三個概念,command、flag和args ,例如:工具

go get -u test.com/a/b

這裏 get 就是commond(這裏比較特殊), -u 就是flag, test.com/a/b 就是args

那麼命令行就是有三部分構成,因此須要定義好這個

  • 命令自身的一些基本信息,用command表示,具體對象是 cobra.Command
  • 命令的一些標緻或者選項,用flag表示,具體對象是 flag.FlagSet
  • 最後的參數,用args表示,一般是[]string

還有一個概念是子命令,好比get就是go的子命令,這是一個樹狀結構的關係。

我可使用go命令,也可使用 go get命令

例如: root.go,定義了root命令,另外在init裏面 定義了flag,若是它自己有具體執行,就填充Run字段。

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
    Use:   "blog",
    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.`,
    // Uncomment the following line if your bare application
    // has an action associated with it:
    //    Run: func(cmd *cobra.Command, args []string) { },
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

func init() {
    cobra.OnInitialize(initConfig)

    // Here you will define your flags and configuration settings.
    // Cobra supports persistent flags, which, if defined here,
    // will be global for your application.

    rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.blog.yaml)")

    // Cobra also supports local flags, which will only run
    // when this action is called directly.
    rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

}

若是須要子命令,就須要在init 裏,給 rootCmd.AddCommand() 其餘的command,其餘的子命令一般也會單獨用一個文件編寫,而且有一個全局變量,讓rootCmd能夠add它

建立子命令

D:\code\github.com\shalk\blog>cobra add  new
new created at D:\code\github.com\shalk\blog

D:\code\github.com\shalk\blog>cobra add  delete
delete created at D:\code\github.com\shalk\blog

D:\code\github.com\shalk\blog>cobra add  list
list created at D:\code\github.com\shalk\blog

D:\code\github.com\shalk\blog>cobra add  edit
edit created at D:\code\github.com\shalk\blog

cmd 目錄下增長了 new.go, delete.go,list.go,edit.go

增長功能代碼

new.go

var newCmd = &cobra.Command{
    Use:   "new",
    Short: "create new post",
    Long:  `create new post `,
    Args: func(cmd *cobra.Command, args []string) error {
        if len(args) != 1 {
            return errors.New("requires a color argument")
        }
        return nil
    },
    Run: func(cmd *cobra.Command, args []string) {
        fileName := "posts/" + args[0]
        err := os.Mkdir("posts", 644)
        if err != nil {
            log.Fatal(err)
        }
        _, err = os.Stat( fileName)
        if os.IsNotExist(err) {
            file, err := os.Create(fileName)
            if err != nil {
                log.Fatal(err)
            }
            log.Printf("create file %s", fileName)
            defer file.Close()
        } else {
        }
    },
}

list.go

var listCmd = &cobra.Command{
    Use:   "list",
    Short: "list all blog in posts",
    Long: `list all blog in posts `,
    Run: func(cmd *cobra.Command, args []string) {
        _, err := os.Stat("posts")
        if os.IsNotExist(err) {
            log.Fatal("posts dir is not exits")
        }
        dirs, err := ioutil.ReadDir("posts")
        if err != nil {
            log.Fatal("read posts dir fail")
        }
        fmt.Println("------------------")
        for _, dir := range dirs {
            fmt.Printf(" %s\n", dir.Name() )
        }
        fmt.Println("------------------")
        fmt.Printf("total: %d blog\n", len(dirs))

    },
}

delete.go

var deleteCmd = &cobra.Command{
    Use:   "delete",
    Short: "delete a post",
    Long: `delete a post`,
    Args: func(cmd *cobra.Command, args []string) error {
        if len(args) != 1 {
            return errors.New("requires a color argument")
        }
        if strings.Contains(args[0],"/") || strings.Contains(args[0],"..") {
            return errors.New("posts name should not contain / or .. ")
        }
        return nil
    },
    Run: func(cmd *cobra.Command, args []string) {
        fileName := "./posts/" +  args[0]
        stat, err := os.Stat(fileName)
        if os.IsNotExist(err) {
            log.Fatalf("post %s is not exist", fileName)
        }
        if stat.IsDir() {
            log.Fatalf("%s is dir ,can not be deleted", fileName)
        }
        err = os.Remove(fileName)
        if err != nil {
            log.Fatalf("delete %s fail, err %v", fileName, err)
        } else {
            log.Printf("delete post %s success", fileName)
        }
    },
}

edit.go 這個有一點麻煩,由於若是調用一個程序好比vim 打開文件,而且golang程序自己要退出,須要detach。暫時放一下(TODO)

編譯測試

我是window測試,linux 更簡單一點

PS D:\code\github.com\shalk\blog> go build -o blog.exe .
PS D:\code\github.com\shalk\blog> .\blog.exe list
------------------
------------------
total: 0 blog
PS D:\code\github.com\shalk\blog> .\blog.exe new blog1.md
2020/07/26 22:37:15 create file posts/blog1.md
PS D:\code\github.com\shalk\blog> .\blog.exe new blog2.md
2020/07/26 22:37:18 create file posts/blog2.md
PS D:\code\github.com\shalk\blog> .\blog.exe new blog3.md
2020/07/26 22:37:20 create file posts/blog3.md
PS D:\code\github.com\shalk\blog> .\blog list
------------------
 blog1.md
 blog2.md
 blog3.md
------------------
total: 3 blog
PS D:\code\github.com\shalk\blog> .\blog delete blog1.md
2020/07/26 22:37:37 delete post ./posts/blog1.md success
PS D:\code\github.com\shalk\blog> .\blog list
------------------
 blog2.md
 blog3.md
------------------
total: 2 blog
PS D:\code\github.com\shalk\blog> ls .\posts\


    目錄: D:\code\github.com\shalk\blog\posts


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        2020/7/26     22:37              0 blog2.md
-a----        2020/7/26     22:37              0 blog3.md


PS D:\code\github.com\shalk\blog>

小結

cobra 是一個高效的命令行解析庫,利用cobra的腳手架,能夠快速的實現一個命令行工具。若是須要更細緻的控制,能夠參考cobra的官方文檔

相關文章
相關標籤/搜索