您好,欢迎访问一九零五行业门户网

使用 Go 来构建一个 CLI 程序

您或许已经围绕 go 语法进行了一次又一次的练习,但是除非您自己构建了一个应用程序,不然的话是体会不到用 go 编写应用程序的真实触感的.
在这篇博文中,我们将用 go 构建一个 cli 应用程序,我们暂且把它叫做 go-grab-xkcd. 该应用程序从 xkcd 拉取漫画并通过命令行参数为您提供各种操作选项.
我们将仅使用 go 标准库构建整个应用程序而不使用外部依赖.
这个应用程序的想法或许看起来有点秀逗,但目的是实践用 go 编写生产级别的 (某种) 代码而不是想被 google 收购.
最后还有一个额外奖励
注意:本文假设读者已熟悉 go 语法和术语,并处于初学者和中级之间.
让我们先来运行一下这个应用程序,然后再进行操作
$ go-grab-xkcd --helpusage of go-grab-xkcd: -n int comic number to fetch (default latest) -o string print output in format: text/json (default "text") -s save image to current directory -t int client timeout in seconds (default 30)$ go-grab-xkcd -n 323title: ballmer peakcomic no: 323date: 1-10-2007description: apple uses automated schnapps ivs.image: https://imgs.xkcd.com/comics/ballmer_peak.png$ go-grab-xkcd -n 323 -o json{ "title": "ballmer peak", "number": 323, "date": "1-10-2007", "description": "apple uses automated schnapps ivs.", "image": "https://imgs.xkcd.com/comics/ballmer_peak.png"}
你可以通过下载并运行计算机上的应用程序来尝试其他选项.
本教程结束后你将熟悉以下主题内容:
接收命令行参数
json 和 go 结构体之间的相互转换
进行 api 调用
创建文件 (从网络上下载并保存)
字符串操作
以下是项目结构
$ tree go-grab-xkcdgo-grab-xkcd├── client│ └── xkcd.go└── model └── comic.go├── main.go└── go.modgo.mod - go modules file used in go for package managementmain.go - main entrypoint of the applicationcomic.go - go representation of the data as a struct and operations on itxkcd.go - xkcd client for making http calls to the api, parsing response and saving to disk
1: 初始化项目
创建一个 go.mod 文件 -
$ go mod init
这将会有助于你进行包管理 (思考 js 中的 package.json 文件)。
2: xkcd api
xkcd 是令人称赞的,您不需要任何注册或访问密钥就可以使用它们的 api。 打开 xkcd api “文档” 您将会发现有两个端点 -
http://xkcd.com/info.0.json - 获取最新漫画
http://xkcd.com/614/info.0.json - 通过漫画编号获取指定的漫画
以下是这些端点的 json 响应 -
{ "num": 2311, "month": "5", "day": "25", "year": "2020", "title": "confidence interval", "alt": "the worst part is that's the millisigma interval.", "img": "https://imgs.xkcd.com/comics/confidence_interval.png", "safe_title": "confidence interval", "link": "", "news": "", "transcript": ""}
文章相关 xkcd
2: 为漫画创建模型
根据上述 json 响应,我们在 model 包中的 comic.go 中创建一个叫做 comicresponse 的 struct
type comicresponse struct { month string `json:"month"` num int `json:"num"` link string `json:"link"` year string `json:"year"` news string `json:"news"` safetitle string `json:"safe_title"` transcript string `json:"transcript"` alt string `json:"alt"` img string `json:"img"` title string `json:"title"` day string `json:"day"`}
您可以使用 json-to-go 工具从 json 自动生成结构体.
顺便创建另一个结构体,该结构体将用于从我们的应用程序输出数据.
type comic struct { title string `json:"title"` number int `json:"number"` date string `json:"date"` description string `json:"description"` image string `json:"image"`}
将以下两种方法添加到 comicresponse 结构体
// formatteddate 函数将格式化日期元素为一个字符串func (cr comicresponse) formatteddate() string { return fmt.sprintf("%s-%s-%s", cr.day, cr.month, cr.year)}// comic 函数将从 api 接收到的 comicresponse 转换为应用程序的输出格式, comic 结构体func (cr comicresponse) comic() comic { return comic{ title: cr.title, number: cr.num, date: cr.formatteddate(), description: cr.alt, image: cr.img, }}
然后将以下两种方法添加到 comic 结构体
// prettystring 函数创建一个漂亮的 comic 字符串并用于输出func (c comic) prettystring() string { p := fmt.sprintf( "title: %s\ncomic no: %d\ndate: %s\ndescription: %s\nimage: %s\n", c.title, c.number, c.date, c.description, c.image) return p}// json 函数将 comic 结构体转换为 json, 我们将使用 json 字符串作为输出内容func (c comic) json() string { cjson, err := json.marshal(c) if err != nil { return "" } return string(cjson)}
3: 设置 xkcd 客户端发起请求,解析响应并保存到磁盘
在 client 包中创建 xkcd.go 文件.
首先定义一个叫做 comicnumber 自定义类型,数据类型为 int
type comicnumber int
定义常量
const ( // xkcd 的 baseurl baseurl string = "https://xkcd.com" // defaultclienttimeout 是取消请求之前要等待的时间 defaultclienttimeout time.duration = 30 * time.second // 根据 xkcd api, latestcomic 是最新的漫画编号 latestcomic comicnumber = 0)
创建一个结构体 xkcdclient, 它将用于向 api 发出请求.
// xkcdclient 是 xkcd 的客户端结构体type xkcdclient struct { client *http.client baseurl string}// newxkcdclient 创建一个新的 xkcdclientfunc newxkcdclient() *xkcdclient { return &xkcdclient{ client: &http.client{ timeout: defaultclienttimeout, }, baseurl: baseurl, }}
添加以下 4 种方法到 xkcdclient
settimeout()
// settimeout 重写了默认的 clienttimeoutfunc (hc *xkcdclient) settimeout(d time.duration) { hc.client.timeout = d}
fetch()
// fetch 根据提供的漫画编号检索漫画func (hc *xkcdclient) fetch(n comicnumber, save bool) (model.comic, error) { resp, err := hc.client.get(hc.buildurl(n)) if err != nil { return model.comic{}, err } defer resp.body.close() var comicresp model.comicresponse if err := json.newdecoder(resp.body).decode(&comicresp); err != nil { return model.comic{}, err } if save { if err := hc.savetodisk(comicresp.img, "."); err != nil { fmt.println("failed to save image!") } } return comicresp.comic(), nil}
savetodisk()
// savetodisk 下载并保存漫画到本地磁盘func (hc *xkcdclient) savetodisk(url, savepath string) error { resp, err := http.get(url) if err != nil { return err } defer resp.body.close() abssavepath, _ := filepath.abs(savepath) filepath := fmt.sprintf("%s/%s", abssavepath, path.base(url)) file, err := os.create(filepath) if err != nil { return err } defer file.close() _, err = io.copy(file, resp.body) if err != nil { return err } return nil}
buildurl()
func (hc *xkcdclient) buildurl(n comicnumber) string { var finalurl string if n == latestcomic { finalurl = fmt.sprintf("%s/info.0.json", hc.baseurl) } else { finalurl = fmt.sprintf("%s/%d/info.0.json", hc.baseurl, n) } return finalurl}
4: 连接所有
在 main() 函数内部我们链接了所有内容
读取命令行参数
实例化 xkcdclient
使用 xkcdclient 从 api 拉取数据
输出
读取命令行参数
comicno := flag.int( "n", int(client.latestcomic), "comic number to fetch (default latest)",)clienttimeout := flag.int64( "t", int64(client.defaultclienttimeout.seconds()), "client timeout in seconds",)saveimage := flag.bool( "s", false, "save image to current directory",)outputtype := flag.string( "o", "text", "print output in format: text/json",)
flag.parse()
实例化 xkcdclient
xkcdclient := client.newxkcdclient()xkcdclient.settimeout(time.duration(*clienttimeout) * time.second)
使用 xkcdclient 从 api 拉取数据
comic, err := xkcdclient.fetch(client.comicnumber(*comicno), *saveimage)if err != nil { log.println(err)}
输出
if *outputtype == "json" { fmt.println(comic.json())} else { fmt.println(comic.prettystring())}
程序运行如下
$ go run main.go -n 323 -o json
或者将其构建为你的笔记本电脑的二进制可执行文件并运行它
$ go build .$ ./go-grab-xkcd -n 323 -s -o json
可以在这个 github 仓库找到完整的源代码 - go-grab-xkcd
额外奖励
通过使用这个简单的 shell 魔术工具可以依次下载多个漫画
$ for i in {1..10}; do ./go-grab-xkcd -n $i -s; done;
上面的 shell 代码简单地在 for 循环中调用 go-grab-xkcd 命令,由于 xkcd 使用序列整数,因此将 i 值替换为漫画编号作为漫画编号 / id.
推荐教程:《php》《go教程》
以上就是使用 go 来构建一个 cli 程序的详细内容。
其它类似信息

推荐信息