从零开始使用Golang构建高质量的命令行应用程序
命令行应用程序是一个非常有用的工具,可以在终端中执行各种操作,如查看文件,运行脚本等等。在本文中,我们将介绍如何使用Golang构建高质量的命令行应用程序。
技术准备
在开始之前,我们需要安装Golang并设置好环境变量。在命令行中输入以下命令来检查是否安装成功:
```
go version
```
如果显示了Golang的版本号,那么说明已经安装成功。
接下来,我们将使用以下两个Golang库来帮助构建命令行应用程序:
1. Cobra:Cobra是一个用于创建命令行应用程序的库。它提供了创建命令、子命令、标记和其他功能的API。
2. Viper:Viper是一个用于处理配置文件和命令行标记的库。它提供了多种格式的配置文件支持,例如JSON、YAML、TOML等。
安装这两个库非常简单。在命令行中,使用以下命令安装Cobra和Viper:
```
go get -u github.com/spf13/cobra/cobra
go get -u github.com/spf13/viper/viper
```
接下来,我们将使用这两个库来构建一个简单的命令行应用程序。
创建项目
在开始之前,我们需要创建一个新的Golang项目。在命令行中,输入以下命令来创建一个名为“mycli”的新项目:
```
mkdir mycli
cd mycli
go mod init mycli
```
在创建了项目之后,我们需要创建一个新的命令行应用程序。
使用以下命令创建一个名为“mycmd”的新命令:
```
cobra init --pkg-name mycmd
```
这将创建一个名为“mycmd”的新目录,并在其中创建一个名为“cmd”的新目录。
```
mycli/
├── cmd/
│ └── mycmd/
│ ├── root.go
│ └── mycmd.go
├── go.mod
└── main.go
```
在“mycmd”目录中,我们可以看到两个文件:root.go和mycmd.go。
root.go文件是应用程序的入口文件。它包含了一些初始化代码,例如创建命令和添加标记。
mycmd.go文件是我们要创建的命令文件。它将包含我们的业务逻辑和处理逻辑。
创建命令
在开始编写代码之前,我们需要先创建一个名为“hello”的新命令。
使用以下命令在mycmd.go中创建一个名为“hello”的新命令:
```
cobra add hello
```
这将在mycmd目录中创建一个名为“hello.go”的新文件,并将新命令添加到root.go中。
```
mycli/
├── cmd/
│ └── mycmd/
│ ├── root.go
│ ├── hello.go // 新增的文件
│ └── mycmd.go
├── go.mod
└── main.go
```
在hello.go文件中,我们可以看到一个名为“helloCmd”的新结构体。这个结构体代表了我们的新命令。
我们可以使用以下代码为命令添加一些元数据:
```
func init() {
helloCmd.PersistentFlags().String("name", "world", "A name to say hello to.")
rootCmd.AddCommand(helloCmd)
}
```
这里我们添加了一个名为“name”的标记,并设置了默认值为“world”。
接下来,我们需要编写一些代码来处理我们的命令。
处理命令
在hello.go文件中,我们可以看到一个名为“runHello”的新函数。这个函数是我们的业务逻辑。
下面是代码实现:
```
func runHello(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
fmt.Printf("Hello, %s!\n", name)
}
```
这个函数从标记中获取名称,并使用fmt包来打印“Hello,name!”的消息。
在main.go文件中,我们可以看到一个名为“Execute”的函数。这个函数是应用程序的入口点。
我们只需要在这个函数中添加以下代码即可:
```
if err := mycmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
```
这个代码将执行应用程序,并处理任何错误。现在,我们已经完成了一个简单的命令行应用程序。
完整代码如下所示:
mycmd/cmd/hello.go:
```
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var helloCmd = &cobra.Command{
Use: "hello",
Short: "Say hello",
Run: runHello,
}
func init() {
helloCmd.PersistentFlags().String("name", "world", "A name to say hello to.")
rootCmd.AddCommand(helloCmd)
}
func runHello(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
fmt.Printf("Hello, %s!\n", name)
}
```
mycmd/cmd/root.go:
```
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "mycmd",
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.`,
Run: func(cmd *cobra.Command, args []string) {
// Do Stuff Here
fmt.Println("Welcome to mycmd!")
},
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.mycmd.yaml)")
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".mycmd" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".mycmd")
}
viper.AutomaticEnv()
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
```
mycmd/main.go:
```
package main
import "mycli/cmd"
func main() {
if err := cmd.Execute(); err != nil {
panic(err)
}
}
```
多格式配置文件支持
在上面的代码中,我们使用了硬编码的标记来设置“name”属性。但是,在实际应用程序中,我们通常会使用配置文件来设置标记。
Viper库支持多种格式的配置文件,例如JSON、YAML、TOML等。我们可以使用以下代码来初始化Viper:
```
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".mycmd" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".mycmd")
}
viper.AutomaticEnv()
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
```
这个代码将首先查找命令行标记中的配置文件,并在找到时使用它。否则,它将查找用户主目录中名为“mycmd”的文件。
以下是我们可以使用的两个示例配置文件:
JSON:
```
{
"hello": {
"name": "Gopher"
}
}
```
YAML:
```
hello:
name: Gopher
```
我们可以使用以下代码在应用程序中读取这些配置:
```
name := viper.GetString("hello.name")
```
结论
在本文中,我们已经学习了如何使用Cobra和Viper构建高质量的命令行应用程序。我们了解了如何创建命令、添加标记、处理命令和处理配置文件。希望这些知识能够帮助你快速构建出自己的命令行应用程序。