Go語言工具簡介 - Honeybadger

banq發表於2020-12-25

在本文中,Ayooluwa Isaiah總結了Go for Rubyists系列,並介紹了go工具。
工具通常被認為是Go生態系統中最強大的方面之一。go命令本身是本文將要討論的許多工具的門戶。透過學習此處討論的每個工具,您在Go專案上工作時將變得更加高效,並快速,可靠地執行常見任務。
 

檢視環境變數
go env命令用於顯示有關當前Go環境的資訊。這是此命令輸出的示例:

GO111MODULE="on"
GOARCH="amd64"
GOBIN="/home/ayo/go/bin"
GOCACHE="/home/ayo/.cache/go-build"
GOENV="/home/ayo/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/ayo/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build145204762=/tmp/go-build -gno-record-gcc-switches"


如果要檢視特定變數的值,可以將它們作為引數傳遞給go env命令:

$ go env GOPATH GOROOT GOBIN
/home/ayo/go
/usr/lib/go
/home/ayo/go/bin

可以使用以下命令訪問每個變數的文件:

go help environmental

 

使用go run執行程式碼
假設您有一個main.go包含以下程式碼的檔案,

package main
import "fmt"
func main() { fmt.Println("Welcome to Go!") }

您可以使用go run命令來執行它,正如我們在本系列中已經多次看到的那樣:

$ go run main.go
Welcome to Go!

該go run命令將編譯程式,在/tmp 目錄中建立可執行檔案,然後一步執行該二進位制檔案。如果要一次執行多個檔案,可以將它們全部作為引數傳遞給go run:

$ go run main.go time.go input.go

或者,您可以使用萬用字元:

$ go run *.go

從Go v1.11開始,您還可以一次執行整個程式包:

$ go run ./foo # Run the package in the `foo` directory
$ go run .     # Run the package in the current directory
 

使用gofmt格式化程式碼
如果您一直在編寫Go程式碼一段時間,就會知道對於如何格式化程式碼有嚴格的約定。gofmt命令對所有現有的Go程式碼強制執行這些約定。
上一節中顯示的程式碼段格式不正確,因此讓我們使用對其進行格式設定gofmt,如下所示:

$ gofmt main.go
package main

import "fmt"

func main() { fmt.Println("Welcome to Go!") }

這將格式化原始檔中的程式碼,並將結果列印到標準輸出中。如果要用格式化的輸出覆蓋原始檔,則需要新增-w標誌。

$ gofmt -w main.go

要遞迴格式化Go原始檔(當前目錄和子目錄),請指定a.作為引數gofmt。

gofmt .

 

修復匯入語句
必須先匯入包,然後才能在程式碼中使用包。否則將失敗,則程式碼將無法編譯,並且將顯示錯誤。給定main.go檔案中的以下程式碼,

package main

func main() {
    fmt.Println("Welcome to Go!")
}


如果嘗試執行該程式,應該看到以下錯誤:

$ go run main.go
# command-line-arguments
./main.go:4:2: undefined: fmt

在編譯程式碼之前,fmt必須先匯入軟體包。您可以手動新增必要的程式碼,也可以使用goimports命令為您新增必要的import語句。

$ goimports main.go
package main

import "fmt"

func main() {
    fmt.Println("Welcome to Go!")
}

該命令還會刪除不再引用的所有匯入軟體包,並以與gofmt相同的樣式設定程式碼格式。因此,您也可以將其go imports 視為的替代品gofmt。

如果將編輯器設定為在儲存Go原始檔時執行,則goimports價值將變得顯而易見。這樣,您就不必擔心在使用軟體包之前匯入軟體包或匯入不再需要的語句。儲存檔案後,它將自動為您完成。大多數程式碼編輯器都有某種外掛或設定可對此提供幫助。
 

構建你的專案
要為您的程式生成可執行二進位制檔案,請使用go build命令。這將在當前目錄中輸出一個二進位制檔案:

$ go build
$ ./demo
Welcome to Go!

生成的二進位制檔案go build特定於您的作業系統體系結構,並且包含執行程式所需的所有內容。因此,您可以將其轉移到具有相同體系結構的另一臺計算機上,即使未安裝Go,它也可以相同的方式執行。

如果要交叉編譯非您自己的體系結構的二進位制檔案,您所需要做的就是 在執行命令之前更改GOOS和GOARCH環境變數的值go build。

例如,以下命令可用於為64位Windows計算機生成二進位制檔案:

$ GOOS=windows GOARCH=amd64 go build

要針對Linux,macOS,ARM,Web Assembly或其他目標進行編譯,請參考Go文件 以檢視GOOS和GOARCH可用的組合。
 

安裝Go二進位制檔案
如果您希望能夠從源目錄外部執行,go install可替代go build。
假設您的main.go檔案位於一個名為的目錄中demo,以下命令將demo在您的$GOPATH/bin目錄中建立一個二進位制檔案。

$ go install

在大多數計算機上$GOPATH應是$HOME/go。您可以使用以下go env命令進行檢查 :

$ go env GOPATH /home/ayo/go

如果要列出 $GOPATH/bin目錄下內,你會看到一個demo二進位制檔案

$ ls $GOPATH/bin
demo

這個二進位制檔案可以在你檔案系統任何位置透過執行demo命令。但是僅當$GOPATH/bin目錄已新增到您$PATH中時,此方法才起作用。

$ demo
Welcome to Go!

 

列出包的資訊
預設呼叫會go list返回您當前所在目錄或提供的包路徑的匯入路徑的名稱:

$ go list
github.com/ayoisaiah/demo
$ go list github.com/joho/godotenv
github.com/joho/godotenv


我們可以使用-f標誌來自定義go list命令的輸出,該標誌使您可以執行Go模板,模板可以訪問go工具的內部資料結構。例如,您可以使用以下命令列出的fmt名稱:

$ go list -f "{{ .Name }}" fmt
fmt

它本身並不是很有趣,但是還有:您可以使用{{ .Imports }}模板列印軟體包的所有依賴項。這是fmt包的輸出:

$ go list -f "{{ .Imports }}" fmt
<p class="indent">[errors internal/fmtsort io math os reflect strconv sync unicode/utf8]


或者,您可以列出軟體包的完整傳遞依賴項集:

$ go list -f "{{ .Deps  }}" fmt
<p class="indent">[errors internal/bytealg internal/cpu internal/fmtsort internal/oserror internal/poll internal/race internal/reflectlite internal/syscall/execenv internal/syscall/unix internal/testlog io math math/bits os reflect runtime runtime/internal/atomic runtime/internal/math runtime/internal/sys sort strconv sync sync/atomic syscall time unicode unicode/utf8 unsafe]


您還可以使用以下go list命令檢查對依賴項和子依賴項的更新:

$ go list -m -u all

或者,檢查對特定依賴項的更新:

$ go list -m -u go.mongodb.org/mongo-driver
go.mongodb.org/mongo-driver v1.1.1 [v1.4.0]

您可以使用該go list命令執行更多操作。可以在documentation查閱可能與該命令一起使用的標誌和其他模板變數。
 

顯示包或符號的文件
go doc命令將列印與由其引數標識的專案關聯的文件註釋。它接受零,一或兩個引數。

要在當前目錄中顯示軟體包的軟體包文件,請使用不帶任何引數的命令:

$ go doc

如果程式包是命令(main程式包),則除非-cmd提供了標誌,否則輸出中將省略匯出符號的文件。

go doc透過將軟體包的匯入路徑作為命令的引數傳遞,我們可以使用該命令檢視任何軟體包的文件:

$ go doc encoding/json
package json // import "encoding/json"

Package json implements encoding and decoding of JSON as defined in RFC
7159. The mapping between JSON and Go values is described in the
documentation for the Marshal and Unmarshal functions.
<p class="indent">[truncated for brevity]

如果要檢視包中特定方法的文件,只需將其作為第二個引數傳遞給go doc:

$ go doc encoding/json Marshal

下面是第二種命令引數,godoc將所有Go軟體包(包括您下載的任何第三方依賴項)的文件顯示為網頁。執行該命令將預設在埠6060上啟動Web伺服器,但是您可以使用該-http標誌更改地址。

$ godoc -http=:6060

 

執行靜態分析
go vet命令可以幫助檢測程式碼中可能未被編譯器捕獲的可疑結構。這些可能未必會阻止您的程式碼編譯,但會影響程式碼質量,例如無法訪問的程式碼,不必要的分配以及格式字串引數格式錯誤。

$ go vet main.go # Run go vet on the `main.go` file
$ go vet .       # Run go vet on all the files in the current directory
$ go vet ./...   # Run go vet recursively on all the files in the current directory


該命令由幾個分析器工具組成,這些工具在此處列出,每個工具都對檔案執行不同的檢查。
預設情況下,go vet執行命令時將執行所有檢查。如果您只想執行特定的檢查(忽略所有其他檢查),則將分析器的名稱作為標記包括在內,並將其設定為true。這是一個printf僅執行檢查的示例 :

$ go vet -printf=true ./...


但是,將-printf=false標誌傳遞給go vet將執行除printf以外的所有檢查。

$ go vet -printf=false ./...

 

向專案新增依賴項
假設您啟用了Go模組go run,則go build,或go install將下載實現程式中import語句所需的所有外部依賴項。預設情況下,如果沒有可用的標記版本,則將下載最新的標記版本或最新的提交。
如果您需要下載依賴關係的特定版本,而不是預設情況下透過Go獲取,則可以使用該go get命令。您可以指定特定版本或提交雜湊:

$ go get github.com/joho/godotenv@v1.2.0 $ go get github.com/joho/godotenv@d6ee687

該方法可根據需要用於升級或降級依賴項。任何下載的依賴項都儲存在位於的模組快取中 $GOPATH/pkg/mod。您可以使用該go clean命令一次性清除所有專案的模組快取:

$ go clean -modcache

 

使用Go模組
這是有效使用模組需要了解的命令的摘要:

  • go mod init 將初始化專案中的模組。
  • go mod tidy清理未使用的依賴項或新增缺失的依賴項。在確認對程式碼進行任何更改之前,請確保執行此命令。
  • go mod download 將所有模組下載到本地快取。
  • go mod vendor將所有第三方依賴項複製到vendor專案根目錄中的資料夾中。
  • go mod edit可用於將go.mod檔案中的依賴項替換為本地或分支版本。例如,如果需要使用派生直到補丁在上游被合併,請使用以下程式碼:


go mod edit -replace=github.com/gizak/termui=github.com/ayoisaiah/termui

 

測試和基準測試程式碼
Go具有一個稱為的內建測試命令go test和一個testing包,可以將其組合以提供簡單但完整的單元測試體驗。該test 工具還包括基準測試和程式碼覆蓋率選項,可幫助您進一步分析程式碼。

讓我們編寫一個簡單的測試來演示該test 工具的一些功能。修改main.go檔案中的程式碼,如下所示:

package main

import "fmt"

func welcome() string {
    return "Welcome!"
}

func main() {
    fmt.Println(welcome())
}

然後,將測試新增到同一目錄中的單獨檔案main_test.go中:

package main

import "testing"

func TestWelcome(t *testing.T) {
    expected := "Welcome to Go!"
    str := welcome()
    if str != expected {
        t.Errorf("String was incorrect, got: %s, want: %s.", str, expected)
    }
}


如果在終端中執行go test,它將失敗:

$ go test
--- FAIL: TestSum (0.00s)
    main_test.go:9: String was incorrect, got: Welcome!, want: Welcome to Go!.
FAIL
exit status 1
FAIL    github.com/ayoisaiah/demo   0.002s


我們可以透過修改檔案中welcome 函式的返回值來使main.go透過測試。


func welcome() string {
    return "Welcome to Go!"
}


現在,測試應該成功透過:

$ go test
PASS
ok      github.com/ayoisaiah/demo   0.002s


如果您有許多具有測試功能的測試檔案,但只想選擇性地執行其中一些,則可以使用該-run標誌。它接受一個正規表示式字串以匹配要執行的測試函式:

$ go test -run=^TestWelcome$ . # Run top-level tests matching "TestWelcome"
$ go test -run= String .       # Run top-level tests matching "String" such as "TestStringConcatenation"

您還應該知道以下測試標誌,這些標誌在測試Go程式時通常會派上用場:
  • -v標誌啟用詳細模式,以便將測試的名稱列印在輸出中。
  • -short標誌跳過長時間執行的測試。
  • -failfast一次失敗測試後,該標誌停止測試。
  • -count標誌連續執行多次測試,這對於您要檢查間歇性故障很有用。

 

程式碼覆蓋率
要檢視程式碼覆蓋狀態,請使用-cover標誌,如下所示:

$ go test -cover
PASS
coverage: 50.0% of statements
ok      github.com/ayoisaiah/demo   0.002s

您還可以使用該-coverprofile標誌生成coverage配置檔案。這使您可以更詳細地研究程式碼覆蓋率結果:

$ go test -coverprofile=coverage.out

coverage.out執行上述命令後,您將在當前目錄中找到一個檔案。該檔案中包含的資訊可用於輸出HTML檔案,其中包含現有測試已覆蓋的確切程式碼行。

$ go tool cover -html=coverage.out

執行此命令時,將彈出一個瀏覽器視窗,其中綠色的覆蓋線和紅色的覆蓋線。
 

標杆管理
Go中的基準測試工具已被廣泛認為是一種衡量Go程式碼效能的可靠方法。_test.go就像測試一樣,基準放置在檔案中。這是一個比較Go中不同字串連線方法的效能的示例:

// main_test.go
package main

import (
    "bytes"
    "strings"
    "testing"
)

var s1 = "random"

const LIMIT = 1000

func BenchmarkConcatenationOperator(b *testing.B) {
    var q string
    for i := 0; i < b.N; i++ {
        for j := 0; j < LIMIT; j++ {
            q = q + s1
        }
        q = ""
    }
    b.ReportAllocs()
}

func BenchmarkStringBuilder(b *testing.B) {
    var q strings.Builder
    for i := 0; i < b.N; i++ {
        for j := 0; j < LIMIT; j++ {
            q.WriteString(s1)
        }
        q.Reset()
    }
    b.ReportAllocs()
}

func BenchmarkBytesBuffer(b *testing.B) {
    var q bytes.Buffer
    for i := 0; i < b.N; i++ {
        for j := 0; j < LIMIT; j++ {
            q.WriteString(s1)
        }
        q.Reset()
    }
    b.ReportAllocs()
}

func BenchmarkByteSlice(b *testing.B) {
    var q []byte
    for i := 0; i < b.N; i++ {
        for j := 0; j < LIMIT; j++ {
            q = append(q, s1...)
        }
        q = nil
    }
    b.ReportAllocs()
}

可以使用以下go test -bench=.命令呼叫此基準測試:

$ go test -bench=.
goos: linux
goarch: amd64
pkg: github.com/ayoisaiah/demo
BenchmarkConcatenationOperator-4        1718        655509 ns/op     3212609 B/op        999 allocs/op
BenchmarkStringBuilder-4              105122         11625 ns/op       21240 B/op         13 allocs/op
BenchmarkBytesBuffer-4                121896          9230 ns/op           0 B/op          0 allocs/op
BenchmarkByteSlice-4                  131947          9903 ns/op       21240 B/op         13 allocs/op
PASS
ok      github.com/ayoisaiah/demo   5.166s


如您所見,串聯運算子是最慢的,每次操作為655509納秒,而bytes.Buffer最快的則為9230納秒。以這種方式編寫基準測試是確定效能改進或可重複性下降的最佳方法。
 

檢測競爭條件
go工具包含一個執行緒競爭探測器,可以透過-race選件啟用 。這對於發現併發系統中的問題很有用,這可能導致崩潰和記憶體損壞。

$ go test -race ./...
$ go run -race main.go
$ go build -race ./...
$ go install -race ./...


 

相關文章