如何把 Go 當(dāng)腳本語言用
點(diǎn)擊上方“Go編程時(shí)光”,選擇“加為星標(biāo)”
第一時(shí)間關(guān)注Go技術(shù)干貨!

Go 作為一種可用于創(chuàng)建高性能網(wǎng)絡(luò)和并發(fā)系統(tǒng)的編程語言,它的生態(tài)應(yīng)用變得越來越廣泛[1],同時(shí),這也激發(fā)了開發(fā)人員使用 Go 作為腳本語言的興趣。雖然目前 Go 還未準(zhǔn)備好作為腳本語言 “開箱即用” 的特性,用來替代 Python 和 Bash ,但是我們只需要一點(diǎn)點(diǎn)準(zhǔn)備工作就可以達(dá)到想要的目標(biāo)。
正如來自 Codelang 的 Elton Minetto 所說的那樣[2],Go 作為一門腳本語言的同時(shí),也具有相當(dāng)大的吸引力,這不僅包括 Go 本身強(qiáng)大的功能和簡(jiǎn)潔的語法,還包括對(duì) goroutines 的良好支持等。Google 公司的軟件工程師 Eyal Posener[3] 為 Go 用作腳本語言提供了更多的理由[4],例如,豐富的標(biāo)準(zhǔn)庫和語言的簡(jiǎn)潔性使得維護(hù)工作變得更加容易。與之相對(duì)的是,Go 的貢獻(xiàn)者和前 Google 公司員工 David Crawshaw 則強(qiáng)調(diào)了使用 Go 編寫腳本任務(wù)的便利程度[5],因?yàn)閹缀跛械某绦騿T都在花費(fèi)大量的時(shí)間編寫復(fù)雜的程序。
基本上,我一直在編寫 Go 程序,偶爾會(huì)寫寫 Bash、perl 和 python 。有時(shí)候,這些編程語言會(huì)落入我的腦海。
對(duì)于日常任務(wù)和不太頻繁的腳本編寫任務(wù),倘若能夠使用相同的編程語言,它將會(huì)大大提高效率。Cloudflare 公司的工程師 Ignat Korchagin 指出,Go 是一種強(qiáng)類型語言,它能夠幫助 Go 腳本變得更加可靠,并且避免出現(xiàn)像拼寫之類的小錯(cuò)誤,從而不會(huì)出現(xiàn)發(fā)生在運(yùn)行時(shí)的報(bào)錯(cuò)[6]。
Codenation 使用 Go 編寫的腳本文件,用來自動(dòng)化地執(zhí)行重復(fù)性的任務(wù),這不僅是開發(fā)流程的一部分,還是其 CI/CD 管道中的任務(wù)。在 Codenation 內(nèi)部,Go 腳本是通過 `go run`[7] 來執(zhí)行的,go run 是 Go 構(gòu)建工具鏈中的默認(rèn)命令,能夠一步一步地編譯和運(yùn)行 Go 程序。Posener 寫道:“事實(shí)上,go run 并非作為解釋器來使用。”
[...] bash 和 python 都是解釋型語言 —— 它們?cè)谧x取腳本的時(shí)候,然后執(zhí)行腳本文件。另一方面,當(dāng)您鍵入
go run時(shí),Go 編譯器就會(huì)編譯程序,然后運(yùn)行它們。Go 程序的編譯時(shí)間很短,這使它看起來就像是解釋型語言一般。
為了讓 Go 編寫的腳本在 shell 腳本程序中表現(xiàn)良好,Codenation 的工程師使用了許多有用的 Go 軟件包:
github.com/fatih/color[8] 是用于輸出對(duì)應(yīng)編碼顏色的包。 github.com/schollz/progressbar[9] 是用于為執(zhí)行時(shí)間過久的任務(wù)創(chuàng)建進(jìn)度條的包。 github.com/jimlawless/whereami[10] 是用于捕獲源代碼的文件名、行號(hào)、函數(shù)等信息的包,這對(duì)于改正錯(cuò)誤信息十分有用! github.com/spf13/cobra[11] 是用于更輕松地創(chuàng)建帶有輸入選項(xiàng)和相關(guān)文檔的復(fù)雜腳本的包。
Crawshaw 寫道:“ 對(duì)于 Codenation 來說,雖然使用命令行工具 go run 來運(yùn)行 Go 程序非常有效,但是它并非最完美的解決方案。” 特別是,Go 缺乏對(duì)讀取-求值-輸出循環(huán) (REPL) 的支持,并且無法輕松地將 Shebang ( 譯者注:Unix 系統(tǒng)中,通常稱 # 為 sharp 或 she;而稱 ! 為 bang ) 集成在一起,這使得腳本可以像二進(jìn)制程序一樣執(zhí)行。此外,比起短小精悍的腳本文件,Go 錯(cuò)誤處理更適合大型程序項(xiàng)目使用。由于這些原因,他開始研究 Neugram[12] ,該項(xiàng)目旨在創(chuàng)建一個(gè) Go 克隆程序用來解決上述所有限制。不巧的是,Neugram 項(xiàng)目現(xiàn)在似乎已經(jīng)被廢棄,這可能是由于 Go 語法的所有細(xì)節(jié)的復(fù)雜性[13]。
Gomacro[14] 項(xiàng)目使用了與 Neugram 類似的方法,它是一種 Go 的解釋器,還支持類似 Lisp 的宏,既可以生成代碼,又可以實(shí)現(xiàn)某種形式的泛型[15]。
Gomacro幾乎是一個(gè)完整的 Go 解釋器,它使用純 Go 語言實(shí)現(xiàn)。它提供了交互式的REPL模式和腳本模式,并且在運(yùn)行時(shí)不需要 Go 構(gòu)建工具鏈。(除了在在一種非常特殊的情況以外:在運(yùn)行時(shí)導(dǎo)入第三方包。)
除了非常適合寫腳本外,Gomacro 還旨在使得 Go 成為一種中間語言,表示要將它解釋為 Go 的標(biāo)準(zhǔn)詳細(xì)規(guī)范[16],還會(huì)提供 Go 源代碼的調(diào)試器[17]。
盡管在使用 Go 編寫腳本的情況下,Gomacro 為其提供了最大靈活性,但不幸的是,它不是標(biāo)準(zhǔn)的 Go 語言,這引起了另一種程度的擔(dān)憂。Posener 對(duì)使用標(biāo)準(zhǔn)的 Go 語言作為腳本語言的可能性進(jìn)行了詳細(xì)分析[18],包括針對(duì)丟失 Shebang 的解決方法。但是,這些解決方法都在某種程度上均有不足的體現(xiàn)。
似乎沒有完美的解決方案,而且我也不明白為什么不能有一個(gè)完美的解決方案。看起來,運(yùn)行 Go 腳本的方式最為簡(jiǎn)單,而最沒有問題的方法就是使用
go run命令。[...] 這就是我為什么認(rèn)為在該語言領(lǐng)域上,仍需要做未完成的工作。同樣的,我認(rèn)為更改程序語言用來忽略Shebang不會(huì)有任何的危害。
但是,對(duì)于 Linux 系統(tǒng),這里可能會(huì)有一個(gè)高級(jí)技巧,能夠在具有完全 Shebang 支持的情況下,從命令行運(yùn)行 Go 腳本。由 Korchagin 舉例子并說明的這種方法,依賴于 Shebang 對(duì) Linux 內(nèi)核的支持,以及從 Linux 用戶空間擴(kuò)展受到支持二進(jìn)制格式的可能性。長(zhǎng)話短說,Korchagin 建議使用以下方式注冊(cè)二進(jìn)制:
$ Echo ':golang:E::go::/usr/local/bin/gorun:OC' | sudo tee /proc/sys/fs/binfmt_misc/register
:golang:E::go::/usr/local/bin/gorun:OC
這樣就可以設(shè)置完全標(biāo)準(zhǔn)的 Go 語言的可執(zhí)行位,例如:
package main
import (
"fmt"
"os"
)
func main() {
s := "world"
if len(os.Args) > 1 {
s = os.Args[1]
}
fmt.Printf("Hello, %v!", s)
fmt.Println("")
if s == "fail" {
os.Exit(30)
}
}
然后執(zhí)行:
$ chmod u+x helloscript.go
$ ./helloscript.go
Hello, world!
$ ./helloscript.go gopher
Hello, gopher!
$ ./helloscript.go fail
Hello, fail!
$ Echo $?
30
盡管這種方法無法提供對(duì) REPL 的支持,但是 Shebang 可能足以滿足典型的用例。
via: https://www.infoq.com/news/2020/04/go-scripting-language/
作者:Sergio De Simone[19]譯者:sunlingbot[20]校對(duì):polaris1119[21]
本文由 GCTT[22] 原創(chuàng)編譯,Go 中文網(wǎng)[23] 榮譽(yù)推出
參考資料
越來越廣泛: https://blog.golang.org/survey2019-results
[2]正如來自 Codelang 的 Elton Minetto 所說的那樣: https://dev.to/codenation/using-golang-as-a-scripting-language-jl2
[3]Eyal Posener: https://posener.github.io/about/
[4]更多的理由: https://gist.github.com/posener/73ffd326d88483df6b1cb66e8ed1e0bd
[5]強(qiáng)調(diào)了使用 Go 編寫腳本任務(wù)的便利程度: https://news.ycombinator.com/item?id=15623106
[6]幫助 Go 腳本變得更加可靠,并且避免出現(xiàn)像拼寫之類的小錯(cuò)誤,從而不會(huì)出現(xiàn)發(fā)生在運(yùn)行時(shí)的報(bào)錯(cuò): https://blog.cloudflare.com/using-go-as-a-scripting-language-in-linux/
[7]go run: https://golang.org/cmd/go/#hdr-Compile_and_run_Go_program
github.com/fatih/color: https://github.com/fatih/color
[9]github.com/schollz/progressbar: https://github.com/schollz/progressbar
[10]github.com/jimlawless/whereami: https://github.com/jimlawless/whereami
[11]github.com/spf13/cobra: https://github.com/spf13/cobra
[12]Neugram: https://github.com/neugram/ng
[13]由于 Go 語法的所有細(xì)節(jié)的復(fù)雜性: https://news.ycombinator.com/item?id=15623244
[14]Gomacro: https://github.com/cosmos72/gomacro
[15]泛型: https://github.com/cosmos72/gomacro#generics
[16]解釋為 Go 的標(biāo)準(zhǔn)詳細(xì)規(guī)范: https://github.com/cosmos72/gomacro/blob/master/doc/code_generation.pdf
[17]提供 Go 源代碼的調(diào)試器: https://github.com/cosmos72/gomacro#debugger
[18]Posener 對(duì)使用標(biāo)準(zhǔn)的 Go 語言作為腳本語言的可能性進(jìn)行了詳細(xì)分析: https://gist.github.com/posener/73ffd326d88483df6b1cb66e8ed1e0bd
[19]Sergio De Simone: https://www.infoq.com/profile/Sergio-De-Simone/
[20]sunlingbot: https://github.com/sunlingbot
[21]polaris1119: https://github.com/polaris1119
[22]GCTT: https://github.com/studygolang/GCTT
[23]Go 中文網(wǎng): https://studygolang.com/
-- END --
???
