用 Golang 寫 Command line 工具

Go-brown-side.sh 如果你要寫 Command line 工具,又想在各平台 (像是 MacOS, Windows 或 Linux) 上執行,這時候 Golang 就是您最好的選擇。在 Reddit 讀到一篇 Command line 工具比較介紹,這篇最主要講到兩個 CLI 工具,一個是 urfave/cli,另一個是 spf13/cobra,這兩個工具其實都非常好用,後者是去年加入 Google Golang 團隊spf13 所開發,該作者加入 Google 後呢,非常的忙,但是強者他同事有幫忙繼續維護 cobra 專案,兩個 CLI 工具各自都有有大型專案使用 urfave/cli 有 docker/libcompose, docker/machine, Drone, Gitea, Gogs 等,而後者 spf13/cobra 則有 docker, docker/distribution, etcd 等。本篇筆者會介紹 urfave/cli 該如何使用?

用 Golang 內建 flag 套件

其實 Golang 本身就有支援 Command line 功能,只要 import flag 就可以直接使用了
package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {
    var showVersion bool

    flag.BoolVar(&showVersion, "version", false, "Print version information.")
    flag.BoolVar(&showVersion, "v", false, "Print version information.")
    flag.Parse()

    // Show version and exit
    if showVersion {
        fmt.Println("Version 1.0.0")
        os.Exit(0)
    }
}
存檔成 main.go,執行 go build -o main 就可以產生 main 執行檔,最後可以直接下 ./main -v 畫面就會顯示 Version 1.0.0。但是如果 flag 非常多,寫起來就會相當長,也不支援讀取 Environment 環境變數,這時候我們可以透過 urfave/cli 來簡化此流程。上面的範例可以在底下連結找到

用 Golang flag 寫 Command Line

使用 urfave/cli 套件

底下是一個簡單範例,可以從 command line 讀取使用者帳號密碼
package main

import (
    "fmt"
    "os"

    "github.com/urfave/cli"
)

type (
    // Config information.
    Config struct {
        username string
        password string
    }
)

var config Config

func main() {
    app := cli.NewApp()
    app.Name = "Example"
    app.Usage = "Example"
    app.Action = run
    app.Flags = []cli.Flag{
        cli.StringFlag{
            Name:  "username,u",
            Usage: "user account",
        },
        cli.StringFlag{
            Name:  "password,p",
            Usage: "user password",
        },
    }

    app.Run(os.Args)
}

func run(c *cli.Context) error {
    config = Config{
        username: c.String("username"),
        password: c.String("password"),
    }

    return exec()
}

func exec() error {
    fmt.Println("username:", config.username)
    fmt.Println("password:", config.password)

    return nil
}
從上面例子可以發現從 Name 就可以定義 Flag 名稱,用逗號分格,在命令列就可以使用 -u--username,也會自動幫忙產生完整的 help 畫面 Screen Shot 2017-02-16 at 2.27.11 PM

支援環境變數

在 Golang 可以快速的將執行檔打包成 Docker Image
FROM centurylink/ca-certs

ADD main /

ENTRYPOINT ["/main"]
在 Dockerfile 內使用參數可以透過 CMD 會變成底下
FROM centurylink/ca-certs

ADD main /

ENTRYPOINT ["/main"]
CMD ["-u", "appleboy"]
這樣非常麻煩,這時候就要讓 CLI 也支援環境變數,將 cli.StringFlag 改成如下
    app.Flags = []cli.Flag{
        cli.StringFlag{
            Name:   "username,u",
            Usage:  "user account",
+           EnvVar: "DOCKER_USERNAME",
        },
        cli.StringFlag{
            Name:   "password,p",
            Usage:  "user password",
+           EnvVar: "DOCKER_PASSWORD",
        },
    }
直接在命令列執行 DOCKER_USERNAME=appleboy ./main,則就會抓到 DOCKER_USERNAME 環境變數,在 Docker 指令就可以補上 -e 參數來實現變數傳遞:
$ docker run -e DOCKER_USERNAME=appleboy appleboy/cli

結論

把 Golang 執行檔包進去 Docker Image,就可以再任意環境內執行,如果你不想使用 Docker Image 也沒關係,Golang 支援跨平台編譯,底下是支援 Windows, Linux, MacOS 編譯參數
    GOOS=linux   GOARCH=amd64 CGO_ENABLED=0 go build -o bin/main-linux
    GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o bin/main.exe
    GOOS=darwin  GOARCH=amd64 CGO_ENABLED=0 go build -o bin/main-darwin
自從學了 Golang,讓用 Windows 工作的同事,也可以享用 Golang 的好處。上述範例檔案可以參考底下連結

用 urfave/cli 寫 Command Line