乐趣区

关于程序员:golang常见库cobra

cobra 是 go 语言的一个库,能够用于编写命令行工具。通常咱们能够看到 git pulldocker container start apt install 等等这样命令,都能够很容易用 corba 来实现,另外,go 语言是很容易编译成一个二进制文件,本文将实现一个简略的命令行工具。

具体写一个例子, 设计一个命令叫 blog,有四个子命令

 blog new [post-name]:创立一篇新的 blog
 blog list:列出以后有哪些文章
 blog delete [post-name]: 删除某一篇文章
 blog edit [post-name]:编辑某一篇文章 

打算有以下几个步骤

  • 创立模块
  • 用 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 里

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

cobra 会装置到 $GOPATH\bin 目录下,留神环境变量中把它退出 PATH 中

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

目录构造如下:

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

编译一下

go build  -o blog .

执行一下

$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 代码的套路。

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 的官网文档。

退出移动版