search
HomeBackend DevelopmentGolangBuild 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"
}

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

1: Initialize project

Create a go.mod file -

$ go mod init

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": ""
}

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"`
}

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"`
}

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,
    }
}

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)
}

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
)

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,
    }
}

Add the following 4 methods to 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: 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",
)

flag.Parse()

Instantiate XKCDClient

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

Use XKCDClient to pull data from API

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

Output

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

The program runs as follows

$ go run main.go -n 323 -o json

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

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

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;

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
This article is reproduced at:learnku. If there is any infringement, please contact admin@php.cn delete
Golang vs. Python: The Pros and ConsGolang vs. Python: The Pros and ConsApr 21, 2025 am 12:17 AM

Golangisidealforbuildingscalablesystemsduetoitsefficiencyandconcurrency,whilePythonexcelsinquickscriptinganddataanalysisduetoitssimplicityandvastecosystem.Golang'sdesignencouragesclean,readablecodeanditsgoroutinesenableefficientconcurrentoperations,t

Golang and C  : Concurrency vs. Raw SpeedGolang and C : Concurrency vs. Raw SpeedApr 21, 2025 am 12:16 AM

Golang is better than C in concurrency, while C is better than Golang in raw speed. 1) Golang achieves efficient concurrency through goroutine and channel, which is suitable for handling a large number of concurrent tasks. 2)C Through compiler optimization and standard library, it provides high performance close to hardware, suitable for applications that require extreme optimization.

Why Use Golang? Benefits and Advantages ExplainedWhy Use Golang? Benefits and Advantages ExplainedApr 21, 2025 am 12:15 AM

Reasons for choosing Golang include: 1) high concurrency performance, 2) static type system, 3) garbage collection mechanism, 4) rich standard libraries and ecosystems, which make it an ideal choice for developing efficient and reliable software.

Golang vs. C  : Performance and Speed ComparisonGolang vs. C : Performance and Speed ComparisonApr 21, 2025 am 12:13 AM

Golang is suitable for rapid development and concurrent scenarios, and C is suitable for scenarios where extreme performance and low-level control are required. 1) Golang improves performance through garbage collection and concurrency mechanisms, and is suitable for high-concurrency Web service development. 2) C achieves the ultimate performance through manual memory management and compiler optimization, and is suitable for embedded system development.

Is Golang Faster Than C  ? Exploring the LimitsIs Golang Faster Than C ? Exploring the LimitsApr 20, 2025 am 12:19 AM

Golang performs better in compilation time and concurrent processing, while C has more advantages in running speed and memory management. 1.Golang has fast compilation speed and is suitable for rapid development. 2.C runs fast and is suitable for performance-critical applications. 3. Golang is simple and efficient in concurrent processing, suitable for concurrent programming. 4.C Manual memory management provides higher performance, but increases development complexity.

Golang: From Web Services to System ProgrammingGolang: From Web Services to System ProgrammingApr 20, 2025 am 12:18 AM

Golang's application in web services and system programming is mainly reflected in its simplicity, efficiency and concurrency. 1) In web services, Golang supports the creation of high-performance web applications and APIs through powerful HTTP libraries and concurrent processing capabilities. 2) In system programming, Golang uses features close to hardware and compatibility with C language to be suitable for operating system development and embedded systems.

Golang vs. C  : Benchmarks and Real-World PerformanceGolang vs. C : Benchmarks and Real-World PerformanceApr 20, 2025 am 12:18 AM

Golang and C have their own advantages and disadvantages in performance comparison: 1. Golang is suitable for high concurrency and rapid development, but garbage collection may affect performance; 2.C provides higher performance and hardware control, but has high development complexity. When making a choice, you need to consider project requirements and team skills in a comprehensive way.

Golang vs. Python: A Comparative AnalysisGolang vs. Python: A Comparative AnalysisApr 20, 2025 am 12:17 AM

Golang is suitable for high-performance and concurrent programming scenarios, while Python is suitable for rapid development and data processing. 1.Golang emphasizes simplicity and efficiency, and is suitable for back-end services and microservices. 2. Python is known for its concise syntax and rich libraries, suitable for data science and machine learning.

See all articles

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

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools

PhpStorm Mac version

PhpStorm Mac version

The latest (2018.2.1) professional PHP integrated development tool

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function