
原文地址:https://alphahinex.github.io/2022/12/11/building-command-line-apps-in-go/
description: "無其他依賴的 write once, run anywhere"
date: 2022.12.11 10:26
categories:
- Go
tags: [Go, Golang]
keywords: golang, cli, urfave/cli, exe, postman, mdnice, gitlab
Write once, run anywhere 是 Sun 1995 年為宣傳 Java 語言的跨平臺特性而提出的口號。
然而了解 Java 的人都知道,這個 run anywhere 是以目標(biāo)環(huán)境存在 JVM 為前提的。當(dāng)我們希望構(gòu)建一個可以 run anywhere 的命令行工具時(shí),Java 顯然不是最好的選擇。
Golang
作為一個更加簡潔、現(xiàn)代的編程語言,Golang 可以通過指定 GOOS 和 GOARCH 兩個環(huán)境變量,將 Go 代碼編譯為目標(biāo)環(huán)境的可執(zhí)行文件(無需 Go 運(yùn)行環(huán)境等其他任何依賴),真正做到 Write Once, (Build multi-times), Run Anywhere。
# 編譯本地環(huán)境可執(zhí)行文件
$ go build
# 編譯 Windows 環(huán)境 64 位 x86 架構(gòu)可執(zhí)行文件
$ GOOS=windows GOARCH=amd64 go build
更多可用的
GOOS和GOARCH組合可參照 https://golang.google.cn/doc/install/source#environment 。
在 Golang 的官網(wǎng)上,也有一個專門的頁面 —— Command-line Interfaces (CLIs) —— 介紹使用 Go 開發(fā)命令行工具的好處,并給出了一些流行的開源項(xiàng)目,以便開發(fā)者能夠更快速的使用 Go 構(gòu)建命令行應(yīng)用:
接下來以 urfave/cli 為例,構(gòu)建一些命令行應(yīng)用。
urfave/cli
按照 https://cli.urfave.org/v2/getting-started/ 中的例子,讓我們創(chuàng)建一個 boom.go 文件
package main
import (
"fmt"
"log"
"os"
"github.com/urfave/cli/v2"
)
func main() {
app := &cli.App{
Name: "boom",
Usage: "make an explosive entrance",
Action: func(*cli.Context) error {
fmt.Println("boom! I say!")
return nil
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
因?yàn)樾枰獙?urfave/cli v2 的包引入進(jìn)來,所以我們需要 使用 Go Modules 。在 boom.go 所在路徑中,執(zhí)行:
$ go mod init example.com/boom
之后按照 安裝文檔 ,添加 urfave/cli v2 模塊:
$ go get github.com/urfave/cli/v2
此時(shí)即可編譯 boom.go 獲得您使用 urfave/cli 構(gòu)建的第一個命令行應(yīng)用了:
$ go build
$ ./boom
boom! I say!
$ ./boom --help
NAME:
boom - make an explosive entrance
USAGE:
boom [global options] command [command options] [arguments...]
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help (default: false)
更多用法可參考 v2 Manual 中的 Examples。
在執(zhí)行
go get github.com/urfave/cli/v2時(shí)如果遇到連接超時(shí)問題,可以按照 https://goproxy.cn/ 中的說明,使用七牛提供的 Go 模塊代理。
Postman
還不怎么會寫 Go 代碼?如果你想構(gòu)建的命令行應(yīng)用是一個調(diào)用三方 RESTful API 實(shí)現(xiàn)一些功能的工具(如下面的 mdnice 樣例),可以先使用 Postman 進(jìn)行接口的調(diào)用和調(diào)試,之后直接生成 go 代碼:

關(guān)于 Postman 的更多用法,可參考 使用 Postman 進(jìn)行系統(tǒng)可接受性測試 。
Sample
提供幾個樣例,源碼地址:https://github.com/AlphaHinex/go-toolkit
random-pick
https://github.com/AlphaHinex/go-toolkit/tree/main/random-pick
隨機(jī)選擇指定類型文件,復(fù)制或移動到指定路徑。
$ ./random-pick -h
NAME:
random-pick - Random pick files in some path
USAGE:
random-pick [global options] command [command options] [arguments...]
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
-n value Pick n files (default: 10)
-t value File type(s) to pick, * means all types, comma separated for multi values: 'jpg,png', case insensitive (default: "*")
-i value Path to pick files (default: ".")
-o value Output picked files (default: ".")
-k Keep picked files in path (default: false)
--help, -h show help (default: false)
從 ./foo 路徑選擇 5 個 jpg 或 png 類型的文件,復(fù)制到 ./bar 路徑:
$ ./random-pick -i ./foo -n 5 -t jpg,png -o ./bar -k
./random-pick -i ./foo -n 5 -t jpg,png -o ./bar -k
Copy ./foo/21670642460.JPG to ./bar/01670679254.JPG
Copy ./foo/31670642460.JPG to ./bar/11670679254.JPG
Copy ./foo/51670642460.JPG to ./bar/21670679254.JPG
Copy ./foo/11670642460.JPG to ./bar/31670679254.JPG
Copy ./foo/71670642460.PNG to ./bar/41670679254.PNG
mdnice
https://github.com/AlphaHinex/go-toolkit/tree/main/mdnice
將指定路徑下的所有圖片文件,上傳至 mdnice 的圖床,需要 mdnice 的 JWT token。圖片在圖床的鏈接以 markdown 格式輸出到圖片來源路徑的 README.md 文件中,上傳失敗的也會將失敗原因記錄至 md 文件。
$ ./mdnice -h
NAME:
mdnice - Upload pictures to mdnice
USAGE:
mdnice [global options] command [command options] [arguments...]
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
-i value Path to be uploaded (default: ".")
--token value Bearer token of mdnice
--token-file value Bearer token file of mdnice
--help, -h show help (default: false)
使用 token 文件中的 JWT Token,將 ./foo 路徑下的所有(圖片)文件上傳:
$ ./mdnice -i ./foo --token-file token
Failed to upload 01670642460.GIF
Upload 01670642460.JPG done
Upload 11670642460.JPG done
Upload 21670642460.JPG done
Upload 31670642460.JPG done
Upload 41670642460.JPG done
Upload 51670642460.JPG done
Upload 61670642460.JPG done
Failed to upload 71670642460.PNG
Upload 81670642460.JPG done
Failed to upload README.md
$ cat ./foo/README.md








---
Upload ./foo/01670642460.GIF failed: 50005:文件過大
Upload ./foo/71670642460.PNG failed: 50005:文件過大
Upload ./foo/README.md failed: 50005:文件類型錯誤,僅支持jpg、jpeg、png、gif、svg類型
gitlab
https://github.com/AlphaHinex/go-toolkit/tree/main/gitlab
調(diào)用 GitLab RESTful API,分析指定項(xiàng)目和分支在某時(shí)間范圍內(nèi)的 Commit 情況,對每個 commit 中修改的文件進(jìn)行逐個分析,統(tǒng)計(jì)新增代碼行數(shù)、減少代碼行數(shù),以及忽略空格和換行改動的新增代碼行數(shù)、減少代碼行數(shù)(相當(dāng)于 git diff -w),將分析結(jié)果生成 csv 文件,并按提交人郵箱進(jìn)行匯總排名,輸出至 console,并可通過飛書機(jī)器人發(fā)送統(tǒng)計(jì)結(jié)果。支持過濾初始 Commit 及 Merge Request Commit。
主要使用了以下兩個 API:
$ ./gitlab -h
NAME:
gitlab - Use GitLab API to do some analysis works
USAGE:
gitlab [global options] command [command options] [arguments...]
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--url value, -u value GitLab host url
--access-token value, -t value Access token to use GitLab API
--project-id value, -p value Project ID in GitLab
--branch value, -b value Branch of project
--since value Date of since, from 00:00:00 (default: "2022-01-01")
--until value Date of until, to 23:59:59 (default: "2022-12-31")
--parallel value Number of commit parsers (default: 16)
--lark value Lark webhook url
--commit-parents commit-parents Only count the commit has commit-parents number parent(s),
-1 means counting all commits,
0 means only counting the initial commit,
2 means only counting merge request commit,
1 means exclude initial commit and merge request commit (default: -1)
--help, -h show help (default: false)
統(tǒng)計(jì) https://gitlab.com/gnachman/iterm2 項(xiàng)目 2022 年 11 月代碼提交情況:
$ ./gitlab -u https://gitlab.com/ -t XXXXXX -p 252461 -b master --commit-parents 1 --since 2022-11-01 --until 2022-11-30
2022/12/10 22:47:19 Start to analyse iterm2 ...
2022/12/10 22:47:22 Load all commits
2022/12/10 22:47:31 Generate 252461_iterm2_master_2022-11-01~2022-11-30.csv use 24.443924546s.
iterm2 項(xiàng)目 master 分支代碼分析結(jié)果(2022-11-01~2022-11-30)
No. author effLines(ratio) effAdds(ratio) commits files
1. gnachman@gmail.com 2366(90.31%) 1538(90.26%) 23 64
2. brewingcode@users.noreply.github.com 2(50.00%) 2(50.00%) 1 2
以上結(jié)果統(tǒng)計(jì)了除初始 Commit 和 Merge Request 外的所有 Commit(時(shí)間范圍內(nèi))
* effLines(有效代碼行數(shù))= 有效增加代碼行數(shù) + 有效減少代碼行數(shù)
* effLines ratio(有效代碼率)= 有效代碼行數(shù) / 總代碼行數(shù) * 100%
* effAdds(有效增加行數(shù))= 有效增加代碼行數(shù)
* effAdds ratio(有效增加率)= 有效增加代碼行數(shù) / 總增加代碼行數(shù) * 100%
* commits:Commit 總數(shù)
* files:文件總數(shù)(不去重)
* 有效代碼:忽略僅有空格或換行的代碼改動,diff -w