用 Go 構(gòu)建全平臺命令行工具

cover

原文地址: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 可以通過指定 GOOSGOARCH 兩個環(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

更多可用的 GOOSGOARCH 組合可參照 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
![](https://files.mdnice.com/user/30377/89e8cb29-4f58-4afc-a9cd-37018de437e3.JPG)
![](https://files.mdnice.com/user/30377/263b7008-eb99-4a0a-b502-2b3b1ceb6e3c.JPG)
![](https://files.mdnice.com/user/30377/4df30bf5-b763-4c94-801b-a7f52573e5c1.JPG)
![](https://files.mdnice.com/user/30377/bf4099ce-b81e-4320-aed7-7501bf06a22f.JPG)
![](https://files.mdnice.com/user/30377/f6183597-1929-42c9-bfa8-962b9521b0c7.JPG)
![](https://files.mdnice.com/user/30377/6f204840-cedd-4d67-909f-ef373bdf5443.JPG)
![](https://files.mdnice.com/user/30377/d42fa98a-0f30-4a87-8a26-32add001aa8d.JPG)
![](https://files.mdnice.com/user/30377/1a5152b1-a665-461e-8324-b58e3209a13a.JPG)

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

  1. /help/api/projects.md
  2. /help/api/commits.md
$ ./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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容