Go语言

Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。

罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及肯·汤普逊(Ken Thompson)于2007年9月开始设计Go,稍后Ian Lance Taylor、Russ Cox加入项目。Go是基于Inferno操作系统所开发的。Go于2009年11月正式宣布推出,成为开放源代码项目,并在Linux及Mac OS X平台上进行了实现,后来追加了Windows系统下的实现。在2016年,Go被软件评价公司TIOBE 选为“TIOBE 2016 年最佳语言”。 目前,Go每半年发布一个二级版本(即从a.x升级到a.y)。

CLI

CLI是Command Line Interface的缩写,即 命令行界面。CLI界面是所有 路由器、TM (Termination Multiplexer, 终端复用器)、CM(Cable Modem, 电缆调制解调器)等产品提供的界面,如 CISCO, LUCENT,Arris, 华为等。它是 路由器产品的标准。使用CLI具有使用要求低(只需要 串行口),容易使用,功能扩充方便等优点,特别是当有很多(如10台)台 路由器设备需要配置的时候,可以通过COPY/PASTE功能很快完成所有路由器的配置。不需要通过IE一台台配置。

在这篇博文中,我们将用 Go 构建一个 CLI 应用程序,我们暂且把它叫做 go-grab-xkcd. 该应用程序从 XKCD 拉取漫画并通过命令行参数为您提供各种操作选项.

我们将仅使用 Go 标准库构建整个应用程序而不使用外部依赖.

这个应用程序的想法或许看起来有点秀逗,但目的是实践用 Go 编写生产级别的 (某种) 代码而不是想被 Google 收购.

最后还有一个额外奖励

让我们先来运行一下这个应用程序,然后再进行操作

$ 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.

以上就是使用 Go 来构建一个 CLI 程序的详细内容,更多请关注亿速云其它相关文章!