当前位置:网站首页>golang语言cli库
golang语言cli库
2022-07-22 02:14:00 【Saggitarxm】
很多用Go写的命令行程序都用了urfave/cli这个库,包括geth,有必要简单了解一下。
用C写过命令行程序的人应该都不陌生,我们需要根据argc/argv一个个地解析命令行参数,调用不同的函数,最后还要写一个usage()函数用于打印帮助信息。urfave/cli把这个过程做了一下封装,抽象出flag/command/subcommand这些模块,用户只需要提供一些模块的配置,参数的解析和关联在库内部完成,帮助信息也可以自动生成。
举个例子,我们想要实现下面这个命令行程序:
NAME:
GoTest - hello world
USAGE:
GoTest [global options] command [command options] [arguments...]
VERSION:
1.2.3
COMMANDS:
help, h Shows a list of commands or help for one command
arithmetic:
add, a calc 1+1
sub, s calc 5-3
database:
db database operations
GLOBAL OPTIONS:
--lang FILE, -l FILE read from FILE (default: "english")
--port value, -p value listening port (default: 8000)
--help, -h Help!Help!
--print-version, -v print version
1. 基本结构
导入包以后,通过cli.NewApp()创建一个实例,然后调用Run()方法就实现了一个最基本的命令行程序了。
当然,为了让我们的程序干点事情,可以指定一下入口函数app.Action,具体写法如下:
import (
"fmt"
"gopkg.in/urfave/cli.v1"
)
func main() {
app := cli.NewApp()
app.Action = func(c *cli.Context) error {
fmt.Println("BOOM!")
return nil
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
2. 公共配置
就是帮助里需要显示的一些基本信息:
app.Name = "GoTest"
app.Usage = "hello world"
app.Version = "1.2.3"
3. Flag配置
具体对应于帮助中的以下信息:
--lang FILE, -l FILE read from FILE (default: "english")
--port value, -p value listening port (default: 8000)
对应代码:
var language string
app.Flags = []cli.Flag {
cli.IntFlag {
Name: "port, p",
Value: 8000,
Usage: "listening port",
},
cli.StringFlag {
Name: "lang, l",
Value: "english",
Usage: "read from `FILE`",
Destination: &language,
},
}
可以看到,每一个flag都对应一个cli.Flag接口的实例。
Name字段中逗号后面的字符表示flag的简写,也就是说"--port"和"-p"是等价的。
Value字段可以指定flag的默认值。
Usage字段是flag的描述信息。
Destination字段可以为该flag指定一个接收者,比如上面的language变量。解析完"--lang"这个flag后会自动存储到这个变量里,后面的代码就可以直接使用这个变量的值了。
另外,如果你想给用户增加一些属性值类型的提示,可以通过占位符(placeholder)来实现,比如上面的"--lang FILE"。占位符通过``符号来标识。
我们可以在app.Action中测试一下打印这些flag的值:
app.Action = func(c *cli.Context) error {
fmt.Println("BOOM!")
fmt.Println(c.String("lang"), c.Int("port"))
fmt.Println(language)
return nil
}
另外,正常来说帮助信息里的flag是按照代码里的声明顺序排列的,如果你想让它们按照字典序排列的话,可以借助于sort:
import "sort"
sort.Sort(cli.FlagsByName(app.Flags))
最后,help和version这两个flag有默认实现,也可以自己改:
cli.HelpFlag = cli.BoolFlag {
Name: "help, h",
Usage: "Help!Help!",
}
cli.VersionFlag = cli.BoolFlag {
Name: "print-version, v",
Usage: "print version",
}
4. Command配置
命令行程序除了有flag,还有command(比如git log, git commit等等)。
另外每个command可能还有subcommand,也就必须要通过添加两个命令行参数才能完成相应的操作。比如我们的db命令包含2个子命令,如果输入GoTest db -h会显示下面的信息:
NAME:
GoTest db - database operations
USAGE:
GoTest db command [command options] [arguments...]
COMMANDS:
insert insert data
delete delete data
OPTIONS:
--help, -h Help!Help!
每个command都对应于一个cli.Command接口的实例,入口函数通过Action指定。如果你想像在帮助信息里实现分组显示,可以为每个command指定一个Category。具体代码如下:
app.Commands = []cli.Command {
{
Name: "add",
Aliases: []string{"a"},
Usage: "calc 1+1",
Category: "arithmetic",
Action: func(c *cli.Context) error {
fmt.Println("1 + 1 = ", 1 + 1)
return nil
},
},
{
Name: "sub",
Aliases: []string{"s"},
Usage: "calc 5-3",
Category: "arithmetic",
Action: func(c *cli.Context) error {
fmt.Println("5 - 3 = ", 5 - 3)
return nil
},
},
{
Name: "db",
Usage: "database operations",
Category: "database",
Subcommands: []cli.Command {
{
Name: "insert",
Usage: "insert data",
Action: func(c *cli.Context) error {
fmt.Println("insert subcommand")
return nil
},
},
{
Name: "delete",
Usage: "delete data",
Action: func(c *cli.Context) error {
fmt.Println("delete subcommand")
return nil
},
},
},
},
}
如果你想在command执行前后执行后完成一些操作,可以指定app.Before/app.After这两个字段:
app.Before = func(c *cli.Context) error {
fmt.Println("app Before")
return nil
}
app.After = func(c *cli.Context) error {
fmt.Println("app After")
return nil
}
具体测试一下:
$ GoTest add
$ GoTest db insert
5. 小结
总体来说,urfave/cli这个库还是很好用的,完成了很多routine的工作,程序员只需要专注于具体业务逻辑的实现。
附完整demo代码:
package cli
import (
"fmt"
"os"
"log"
"sort"
"gopkg.in/urfave/cli.v1"
)
func Run() {
var language string
app := cli.NewApp()
app.Name = "GoTest"
app.Usage = "hello world"
app.Version = "1.2.3"
app.Flags = []cli.Flag {
cli.IntFlag {
Name: "port, p",
Value: 8000,
Usage: "listening port",
},
cli.StringFlag {
Name: "lang, l",
Value: "english",
Usage: "read from `FILE`",
Destination: &language,
},
}
app.Commands = []cli.Command {
{
Name: "add",
Aliases: []string{"a"},
Usage: "calc 1+1",
Category: "arithmetic",
Action: func(c *cli.Context) error {
fmt.Println("1 + 1 = ", 1 + 1)
return nil
},
},
{
Name: "sub",
Aliases: []string{"s"},
Usage: "calc 5-3",
Category: "arithmetic",
Action: func(c *cli.Context) error {
fmt.Println("5 - 3 = ", 5 - 3)
return nil
},
},
{
Name: "db",
Usage: "database operations",
Category: "database",
Subcommands: []cli.Command {
{
Name: "insert",
Usage: "insert data",
Action: func(c *cli.Context) error {
fmt.Println("insert subcommand")
return nil
},
},
{
Name: "delete",
Usage: "delete data",
Action: func(c *cli.Context) error {
fmt.Println("delete subcommand")
return nil
},
},
},
},
}
app.Action = func(c *cli.Context) error {
fmt.Println("BOOM!")
fmt.Println(c.String("lang"), c.Int("port"))
fmt.Println(language)
// if c.Int("port") == 8000 {
// return cli.NewExitError("invalid port", 88)
// }
return nil
}
app.Before = func(c *cli.Context) error {
fmt.Println("app Before")
return nil
}
app.After = func(c *cli.Context) error {
fmt.Println("app After")
return nil
}
sort.Sort(cli.FlagsByName(app.Flags))
cli.HelpFlag = cli.BoolFlag {
Name: "help, h",
Usage: "Help!Help!",
}
cli.VersionFlag = cli.BoolFlag {
Name: "print-version, v",
Usage: "print version",
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
更多文章欢迎关注“鑫鑫点灯”专栏:https://blog.csdn.net/turkeycock/article/category/7669858
边栏推荐
猜你喜欢
Distributed link tracking skywalking practice
Redis cache penetration and avalanche
Let security move | no matter what industry network architecture, these six tactics win the target
PLT draw and save the results
Redis master-slave replication
Introduction to microservices
微服务简介
21jvm memory model (JMM)
高压差分探头导致的驱动电压离谱的原因
Record online double write failure log MySQL error troubleshooting reasons
随机推荐
优炫数据库上可以搭建Oracle RAC吗?
FPGA图像处理学习——人脸识别
Rebound shell carries out suid authorization through ordinary users
Open source demo | release of open source example of arcall applet
【07】函数调用:为什么会发生stack overflow?
Redis master-slave replication
1.元函数与type_traits
配置Eureka时Status显示的是电脑名而不是localhost及ipAddr显示为本机ip的问题
如何正确使用电流探头
How to use first-hand data visualization to win the favor of the boss and grasp the key points of data visualization
DM8: query the data file size limit of Dameng database
华为云从入门到实战 | AI云开发ModelArts入门与WAF应用与部署
92. (leaflet chapter) leaflet situation plotting - acquisition of attack direction
Bi analytical thinking of business intelligence: cash cycle of manufacturing industry (II)
Silicon Valley class notes (middle)
用R写一个迷宫小游戏
1. Meta function and type_ traits
Summary of URL rules for real-time streaming of Hikvision, Dahua and yushila
[Huawei machine test questions] maximum number of components [2022 Q3 | 100 points]
【C语言-文件】数据终于可以出内存,到外面的世界看看了/(ㄒoㄒ)/~~