Home Backend Development Golang Build a CLI program using Go

Build a CLI program using Go

Jun 12, 2020 pm 06:28 PM
golang

Build a CLI program using Go

You may have practiced Go syntax again and again, but unless you build an application yourself, you have not experienced the real feel of writing applications in Go. .

In this blog post, we will build a CLI application in Go, let’s call it go-grab-xkcd. The application pulls comics from XKCD and provides them for you through command line parameters Provide various operation options.

We will build the entire application using only the Go standard library without using external dependencies.

The idea of ​​this application may seem a bit silly, but the purpose is Practice writing production-level (sort of) code in Go without trying to get acquired by Google.

There’s a bonus at the end

Note: This article assumes that the reader is already familiar with Go syntax and terminology, and Somewhere between beginner and intermediate.

Let's run the app first and then move on

$ 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"
}
Copy after login

You can try other options by downloading and running the app on your computer .

After the end of this tutorial you will be familiar with the following topics:

Receiving command line parameters

Converting between JSON and Go structures

Make API calls

Create files (download and save from the network)

String operations

The following is the project structure

$ 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
Copy after login

1: Initialize project

Create a go.mod file -

$ go mod init
Copy after login

This will help you with package management (think package.json files in JS).

2: xkcd API

xkcd is amazing and you don’t need any registration or access keys to use their API. Open the xkcd API "Documentation" and you will find that there are two endpoints -

http://xkcd.com/info.0.json - Get the latest comics

http://xkcd. com/614/info.0.json - Get the specified comic by comic number

The following is the JSON response of these endpoints-

{
  "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": ""
}
Copy after login

Article related xkcd

2: Comic creation model

According to the above JSON response, we create a struct called ComicResponse in comic.go in the model package

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"`
}
Copy after login

You can use the JSON-to-Go tool to automatically generate it from JSON Structure.

By the way create another structure that will be used to output data from our application.

type Comic struct {
    Title       string `json:"title"`
    Number      int    `json:"number"`
    Date        string `json:"date"`
    Description string `json:"description"`
    Image       string `json:"image"`
}
Copy after login

Add the following two methods to the ComicResponse structure

// 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,
    }
}
Copy after login

Then add the following two methods to the Comic structure

// 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)
}
Copy after login

3: Set up the xkcd client to initiate a request, parse the response and save it to disk

Create xkcd in the client package. go file.

First define a custom type called ComicNumber, the data type is int

type ComicNumber int

Define constants

const (
    // xkcd 的 BaseURL
    BaseURL string = "https://xkcd.com"
    // DefaultClientTimeout 是取消请求之前要等待的时间
    DefaultClientTimeout time.Duration = 30 * time.Second
    // 根据 xkcd API, LatestComic 是最新的漫画编号
    LatestComic ComicNumber = 0
)
Copy after login

Create a structure XKCDClient, which will be used to make requests to the 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,
    }
}
Copy after login

Add the following 4 methods to XKCDClient

SetTimeout()

// SetTimeout 重写了默认的 ClientTimeout
func (hc *XKCDClient) SetTimeout(d time.Duration) {
    hc.client.Timeout = d
}
Copy after login

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
}
Copy after login

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
}
Copy after login

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
}
Copy after login

4: Connect all

Inside the main() function we link all the content

read Command line parameters

Instantiate XKCDClient

Use XKCDClient to pull data from the API

Output

Read command line parameters

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",
)
Copy after login

flag.Parse()

Instantiate XKCDClient

xkcdClient := client.NewXKCDClient()
xkcdClient.SetTimeout(time.Duration(*clientTimeout) * time.Second)
Copy after login

Use XKCDClient to pull data from API

comic, err := xkcdClient.Fetch(client.ComicNumber(*comicNo), *saveImage)
if err != nil {
    log.Println(err)
}
Copy after login

Output

if *outputType == "json" {
    fmt.Println(comic.JSON())
} else {
    fmt.Println(comic.PrettyString())
}
Copy after login

The program runs as follows

$ go run main.go -n 323 -o json
Copy after login

Or build it as a binary executable on your laptop and run it

$ go build .
$ ./go-grab-xkcd -n 323 -s -o json
Copy after login

The complete source code can be found in this Github repository - go-grab-xkcd

Extra bonus

You can download multiple comics in sequence by using this simple shell magic tool

$ for i in {1..10}; do ./go-grab-xkcd -n $i -s; done;
Copy after login

The above shell code simply calls go-grab-xkcd in a for loop command, since xkcd uses sequence integers, replace the i value with the comic number as the comic number/ID.

Recommended tutorials: "PHP" "Go Tutorial"

The above is the detailed content of Build a CLI program using Go. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to safely read and write files using Golang? How to safely read and write files using Golang? Jun 06, 2024 pm 05:14 PM

Reading and writing files safely in Go is crucial. Guidelines include: Checking file permissions Closing files using defer Validating file paths Using context timeouts Following these guidelines ensures the security of your data and the robustness of your application.

How to configure connection pool for Golang database connection? How to configure connection pool for Golang database connection? Jun 06, 2024 am 11:21 AM

How to configure connection pooling for Go database connections? Use the DB type in the database/sql package to create a database connection; set MaxOpenConns to control the maximum number of concurrent connections; set MaxIdleConns to set the maximum number of idle connections; set ConnMaxLifetime to control the maximum life cycle of the connection.

Golang framework vs. Go framework: Comparison of internal architecture and external features Golang framework vs. Go framework: Comparison of internal architecture and external features Jun 06, 2024 pm 12:37 PM

The difference between the GoLang framework and the Go framework is reflected in the internal architecture and external features. The GoLang framework is based on the Go standard library and extends its functionality, while the Go framework consists of independent libraries to achieve specific purposes. The GoLang framework is more flexible and the Go framework is easier to use. The GoLang framework has a slight advantage in performance, and the Go framework is more scalable. Case: gin-gonic (Go framework) is used to build REST API, while Echo (GoLang framework) is used to build web applications.

How to save JSON data to database in Golang? How to save JSON data to database in Golang? Jun 06, 2024 am 11:24 AM

JSON data can be saved into a MySQL database by using the gjson library or the json.Unmarshal function. The gjson library provides convenience methods to parse JSON fields, and the json.Unmarshal function requires a target type pointer to unmarshal JSON data. Both methods require preparing SQL statements and performing insert operations to persist the data into the database.

How to find the first substring matched by a Golang regular expression? How to find the first substring matched by a Golang regular expression? Jun 06, 2024 am 10:51 AM

The FindStringSubmatch function finds the first substring matched by a regular expression: the function returns a slice containing the matching substring, with the first element being the entire matched string and subsequent elements being individual substrings. Code example: regexp.FindStringSubmatch(text,pattern) returns a slice of matching substrings. Practical case: It can be used to match the domain name in the email address, for example: email:="user@example.com", pattern:=@([^\s]+)$ to get the domain name match[1].

Transforming from front-end to back-end development, is it more promising to learn Java or Golang? Transforming from front-end to back-end development, is it more promising to learn Java or Golang? Apr 02, 2025 am 09:12 AM

Backend learning path: The exploration journey from front-end to back-end As a back-end beginner who transforms from front-end development, you already have the foundation of nodejs,...

How to use predefined time zone with Golang? How to use predefined time zone with Golang? Jun 06, 2024 pm 01:02 PM

Using predefined time zones in Go includes the following steps: Import the "time" package. Load a specific time zone through the LoadLocation function. Use the loaded time zone in operations such as creating Time objects, parsing time strings, and performing date and time conversions. Compare dates using different time zones to illustrate the application of the predefined time zone feature.

Golang framework development practical tutorial: FAQs Golang framework development practical tutorial: FAQs Jun 06, 2024 am 11:02 AM

Go framework development FAQ: Framework selection: Depends on application requirements and developer preferences, such as Gin (API), Echo (extensible), Beego (ORM), Iris (performance). Installation and use: Use the gomod command to install, import the framework and use it. Database interaction: Use ORM libraries, such as gorm, to establish database connections and operations. Authentication and authorization: Use session management and authentication middleware such as gin-contrib/sessions. Practical case: Use the Gin framework to build a simple blog API that provides POST, GET and other functions.

See all articles