首頁  >  文章  >  後端開發  >  使用 Go 來建立一個 CLI 程序

使用 Go 來建立一個 CLI 程序

Guanhui
Guanhui轉載
2020-06-12 18:28:173547瀏覽

使用 Go 來建立一個 CLI 程序

您或許已經圍繞Go 語法進行了一次又一次的練習,但是除非您自己構建了一個應用程序,不然的話是體會不到用Go 編寫應用程序的真實觸感的.

在這篇博文中,我們將用Go 構建一個CLI 應用程序,我們暫且把它叫做go-grab-xkcd. 該應用程序從XKCD 拉取漫畫並通過命令行參數為您提供各種操作選項.

我們將僅使用Go 標準庫構建整個應用程式而不使用外部依賴.

這個應用程式的想法或許看起來有點秀逗,但目的是實踐用Go 寫生產等級的(某種) 程式碼而不是想被Google 收購.

最後還有一個額外獎勵

注意:本文假設讀者已熟悉Go 語法和術語,並處於初學者和中級之間.

讓我們先來運行一下這個應用程序,然後再進行操作

$ go-grab-xkcd --help
Usage 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 323
Title: Ballmer Peak
Comic No: 323
Date: 1-10-2007
Description: 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-xkcd
go-grab-xkcd
├── client
│   └── xkcd.go
└── model
    └── comic.go
├── main.go
└── go.mod
go.mod - Go Modules file used in Go for package management
main.go - Main entrypoint of the application
comic.go - Go representation of the data as a struct and operations on it
xkcd.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 创建一个新的 XKCDClient
func NewXKCDClient() *XKCDClient {
    return &XKCDClient{
        client: &http.Client{
            Timeout: DefaultClientTimeout,
        },
        baseURL: BaseURL,
    }
}

添加以下4 種方法到XKCDClient

SetTimeout()

// SetTimeout 重写了默认的 ClientTimeout
func (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 程序的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除
上一篇:Go String 解析下一篇:Go String 解析