大家好,我是谢伟,是一名程序员。
过去一阵子,我在开发一款客户端命令行工具,业余时间,开始写了下面这个工具。仅做学习参考使用。现在它看上去不够优雅,命令的命名也没有好好推敲。但功能都已实现。
即如何构建一个命令行工具,希望通过这个项目的示例,你能开发出各种各样符合你需求的命令行工具。
比如 github 上非常热门的命令行项目:
- annie 下载视频和图片工具
- hub 一个包装git 操作github 的工具
- jfrog-cli-go 一个仓库管理平台的客户端
...
开始之前,还是看几个命令行工具一般是啥样的:
beego: 命令行工具bee
λ bee
Bee is a Fast and Flexible tool for managing your Beego Web Application.
USAGE
bee command [arguments]
AVAILABLE COMMANDS
version Prints the current Bee version
migrate Runs database migrations
api Creates a Beego API application
bale Transforms non-Go files to Go source files
fix Fixes your application by making it compatible with newer versions of Beego
dlv Start a debugging session using Delve
dockerize Generates a Dockerfile for your Beego application
generate Source code generator
hprose Creates an RPC application based on Hprose and Beego frameworks
new Creates a Beego application
pack Compresses a Beego application into a single file
rs Run customized scripts
run Run the application by starting a local development server
server serving static content over HTTP on port
Use bee help [command] for more information about a command.
ADDITIONAL HELP TOPICS
Use bee help [topic] for more information about that topic.
思考一个问题:一个好的命令行应该具备什么?
- 完善的帮助命令
- 优雅的输出格式
- 支持长、短参数
- 命令补全
- 支持表格输出
等等
实质:客户端命令行工具的实质是 接口或者API 的封装。
1、如何解析命令行参数
- os.Args
- Flag
- cli
1. os.Args:
第一个参数是: 文件名称,之外的参数是命令行接收的参数
对参数进行处理即可实现解析命令行参数。
args = os.Args
oneArg = args[1]
TwoArg = args[2]
func add() int {
number_one, _ := strconv.Atoi(args[2])
number_two, _ := strconv.Atoi(args[3])
return number_one + number_two
}
2. Flag
Flag golang 系统自带的库能更好的处理命令行参数:
var operation string
var numberone float64
var numbertwo float64
flag.StringVar(&operation, "o", "add", "operation for this tool")
flag.Float64Var(&numberone, "n1", 0, "The first number")
flag.Float64Var(&numbertwo, "n2", 0, "The second number")
定义三个参数,分别为string、float、float 类型
3. cli
这是一个第三方库,是一个命令参数解析的框架,能够很好的快速形成命令行。
cli项目地址
主要包括:
- app 主要实现的是整体的命令行工具动作
- command 对命令的处理
- flag 对短参数的处理
- help 使用 template 模板实现命令的帮助提示
2、如何组织项目
- commands : 命令集合
- domain: http 请求处理
- main: 程序入口
- objects: 定义结构体
- reader : 命令清单入口
- utils: 程序帮助程序入口
3、如何组织命令
第一级命令集合
func Commands() []cli.Command {
return []cli.Command{
{
Name: "book",
Usage: douban.BookUsage,
Subcommands: douban.SubBookCommand(),
},
{
Name: "story",
Usage: one.StoryUsage,
Subcommands: one.SubStoryCommand(),
},
{
Name: "movie",
Usage: douban.MovieUsage,
Subcommands: douban.SubMovieCommand(),
},
}
}
第二级命令集合:
func SubBookCommand() []cli.Command {
return []cli.Command{
{
Name: "random",
Action: actionBookNumber,
},
{
Name: "detail",
Action: actionBookDetail,
},
{
Name: "search",
Flags: getFlagSearch(),
Subcommands: subCommandBookSearch(),
},
}
}
第三级命令集合:
func subCommandBookSearch() []cli.Command {
return []cli.Command{
{
Name: "query",
Action: actionQuery,
},
{
Name: "tag",
Action: actionTag,
},
}
}
组合起来即是: YiYi.exe book random | detail | search query | search tag ...
可以看出该框架对命令的组织方式很优雅。可以让使用者聚焦在实现业务上。
4、操作步骤
- 组织命令
- 实现具体函数处理
这里以:YiYi.exe book search query arg
这个命令讲述如何实现。
实例化APP
func main(){
app := cli.NewApp()
app.CommandNotFound = func(context *cli.Context, command string) {
fmt.Printf("[[WARNING] Not Found Command: %s\n", command)
fmt.Printf("[MESSAGE] Please Type: Reader --help")
}
app.Commands = reader.Commands()
app.Run(os.Args)
}
定义Commands
func Commands() []cli.Command {
return []cli.Command{
{
Name: "book",
Usage: douban.BookUsage,
Subcommands: douban.SubBookCommand(),
},
}
}
定义 SubCommand
func SubBookCommand() []cli.Command {
return []cli.Command{
{
Name: "search",
Flags: getFlagSearch(),
Subcommands: subCommandBookSearch(),
},
}
}
func subCommandBookSearch() []cli.Command {
return []cli.Command{
{
Name: "query",
Action: actionQuery,
},
{
Name: "tag",
Action: actionTag,
},
}
}
实现 search query arg 命令
func actionQuery(c *cli.Context) {
if c.NArg() == 1 {
//fmt.Println(c.String("count"))
url := fmt.Sprintf(bookSearchByQuery, c.Args().Get(0), strconv.Itoa(c.Int("count")))
getBookSearch(url)
}
}
具体实现:
结构体 和 模板输出
type BookAll struct {
BookCollection []BookInfo
}
type BookInfo struct {
Title string
Subtitle string
URL string
Isbn10 string
Isbn13 string
Price string
}
func (b BookAll) Template() {
t := template.New("New Template for book")
t, _ = t.Parse(`
Show Book from DouBan Api:
AllCollections:
{{range .BookCollection}}
Title: {{.Title}}
Subtitle: {{.Subtitle}}
URL: {{.URL}}
Isbn10: {{.Isbn10}}
Isbn13: {{.Isbn13}}
Price: {{.Price}}
{{end}}
`)
t.Execute(os.Stdout, b)
}
具体http 请求处理:
func getBookSearch(url string) {
var allBook []objects.BookInfo
data := utils.Response(url, "books")
for _, one := range data.Array() {
var oneBook objects.BookInfo
oneBook.Title = one.Get("title").String()
oneBook.Subtitle = one.Get("subtitle").String()
oneBook.Isbn10 = one.Get("isbn10").String()
oneBook.Isbn13 = one.Get("isbn13").String()
oneBook.URL = one.Get("url").String()
oneBook.Price = one.Get("price").String()
allBook = append(allBook, oneBook)
}
AllData := objects.BookAll{
BookCollection: allBook,
}
AllData.Template()
}
func Response(url string, key string) gjson.Result {
var newClient httpclient.HttpClient
newClient = httpclient.Implement{}
resp, err := newClient.Get(url)
//fmt.Println(url)
if err != nil {
fmt.Println("Get HTTP Response Failed")
return gjson.Result{}
}
if key == "" {
return gjson.Parse(string(resp))
} else {
return gjson.Parse(string(resp)).Get(key)
}
}
gjson 处理 是用来处理 json 的第三方库,非常好用。
上面我们使用框架,一级一级实现下来,发现实际上我们只要聚焦实现业务:即http 请求 和 响应信息的处理即可。
5. 效果
YiYi.exe --help
YiYi is a tool for reading with DouBan and One APP api.
NAME:
YiYi - An application for book, movie, and story from DouBan and One App.
USAGE:
YiYi [global options] command [command options] [arguments...]
VERSION:
___ ___ ___ ___
/\__\ /\ \ /\__\ /\ \
|::L__L _\:\ \ |::L__L _\:\ \
|:::\__\ /\/::\__\ |:::\__\ /\/::\__\
/:;;/__/ \::/\/__/ /:;;/__/ \::/\/__/
\/__/ \:\__\ \/__/ \:\__\
\/__/ \/__/ v0.0.1
DESCRIPTION:
An application for book, movie, and story from DouBan and One App.
AUTHOR:
xieWei
COMMANDS:
book get book info from DouBan API
story get story info from One API
movie get movie info from DouBan API
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
YiYi.exe book search query Golang
返回信息
Show Book from DouBan Api:
AllCollections:
Title: Cloud Native programming with Golang: Develop microservice-based high performance web apps for the cloud with Go
Subtitle: Discover practical techniques to build cloud-native apps that are scalable, reliable, and always available.
URL: https://api.douban.com/v2/book/30154908
Isbn10: 178712598X
Isbn13: 9781787125988
Price: USD 44.99
Title: Building RESTful Web services with Go: Learn how to build powerful RESTful APIs with Golang that scale gracefully
Subtitle: Explore the necessary concepts of REST API development by building few real world services from scratch.
URL: https://api.douban.com/v2/book/30154905
Isbn10: 1788294289
Isbn13: 9781788294287
Price: USD 44.99
Title: Go Standard Library Cookbook: Over 120 specific ways to make full use of the standard library components in Golang
Subtitle: Implement solutions by leveraging the power of the GO standard library and reducing dependency on external crates
URL: https://api.douban.com/v2/book/30179004
Isbn10: 1788475275
Isbn13: 9781788475273
Price: USD 49.99
Title: Go语言编程
Subtitle:
URL: https://api.douban.com/v2/book/11577300
Isbn10: 7115290369
Isbn13: 9787115290366
Price: 49.00元
Title: The Go Programming Language
Subtitle:
URL: https://api.douban.com/v2/book/26337545
Isbn10: 0134190440
Isbn13: 9780134190440
Price: USD 39.99
Title: Go Web编程
Subtitle:
URL: https://api.douban.com/v2/book/24316255
Isbn10: 7121200910
Isbn13: 9787121200915
Price: 65.00元
Title: Go语言学习笔记
Subtitle:
URL: https://api.douban.com/v2/book/26832468
Isbn10: 7121291606
Isbn13: 9787121291609
Price: 89
Title: Go 语言程序设计
Subtitle:
URL: https://api.douban.com/v2/book/24869910
Isbn10: 7115317909
Isbn13: 9787115317902
Price: CNY 69.00
Title: Go程序设计语言
Subtitle:
URL: https://api.douban.com/v2/book/27044219
Isbn10: 7111558421
Isbn13: 9787111558422
Price: 79
Title: Go并发编程实战
Subtitle: Go并发编程实战
URL: https://api.douban.com/v2/book/26244729
Isbn10: 7115373981
Isbn13: 9787115373984
Price: 89元
Title: Go in Action
Subtitle:
URL: https://api.douban.com/v2/book/25858023
Isbn10: 1617291781
Isbn13: 9781617291784
Price: USD 39.99
具体用法:
YiYi 帮助文档
项目地址