# Go 命令

## Go 命令

Go 語言自帶有一套完整的命令列工具，你可以透過在命令列中執行 `go` 來檢視它們：

![](https://4278675773-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6Ees3f-VcmFu88247C%2Fsync%2F45ffd924013bc84c7f5ddb3339449d64de22bd02.png?generation=1588327078851625\&alt=media)

圖 1.3 Go 命令顯示詳細的資訊

這些命令對於我們平時編寫的程式碼非常有用，接下來就讓我們了解一些常用的命令。

## go build

這個命令主要用於編譯程式碼。在套件的編譯過程中，若有必要，會同時編譯與之相關聯的套件。

* 如果是普通套件，就像我們在 1.2 節中編寫的 `mymath` 套件那樣，當你執行`go build`之後，它不會產生任何檔案。如果你需要在`$GOPATH/pkg`下產生相應的檔案，那就得執行`go install`。
* 如果是 `main` 套件，當你執行`go build`之後，它就會在當前目錄下產生一個可執行檔案。如果你需要在`$GOPATH/bin`下產生相應的檔案，需要執行`go install`，或者使用`go build -o 路徑/a.exe`。
* 如果某個專案資料夾下有多個檔案，而你只想編譯某個檔案，就可在`go build`之後加上檔名，例如`go build a.go`；`go build`命令預設會編譯當前目錄下的所有 go 檔案。
* 你也可以指定編譯輸出的檔名。例如 1.2 節中的 `mathapp` 應用，我們可以指定`go build -o astaxie.exe`，預設情況是你的 package 名(非 main 套件)，或者是第一個原始檔的檔名(main 套件)。

  （注：實際上，package 名在 [Go 語言規範](https://golang.org/ref/spec)中指程式碼中“package”後使用的名稱，此名稱可以與資料夾名不同。預設產生的可執行檔名是資料夾名。）
* go build 會忽略目錄下以“\_”或“.”開頭的 go 檔案。
* 如果你的原始碼針對不同的作業系統需要不同的處理，那麼你可以根據不同的作業系統字尾來命名檔案。例如有一個讀取陣列的程式，它對於不同的作業系統可能有如下幾個原始檔：

  array\_linux.go array\_darwin.go array\_windows.go array\_freebsd.go

  `go build`的時候會選擇性地編譯以系統名結尾的檔案（Linux、Darwin、Windows、Freebsd）。例如 Linux 系統下面編譯只會選擇 array\_linux.go 檔案，其它系統命名字尾檔案全部忽略。

參數的介紹

* `-o` 指定輸出的檔名，可以帶上路徑，例如 `go build -o a/b/c`
* `-i` 安裝相應的套件，編譯+`go install`
* `-a` 更新全部已經是最新的套件的，但是對標準套件不適用
* `-n` 把需要執行的編譯命令顯示出來，但是不執行，這樣就可以很容易的知道底層是如何執行的
* `-p n` 指定可以並行可執行的編譯數目，預設是 CPU 數目
* `-race` 開啟編譯的時候自動檢測資料競爭的情況，目前只支援 64 位的機器
* `-v` 顯示出來我們正在編譯的套件名
* `-work` 顯示出來編譯時候的臨時資料夾名稱，並且如果已經存在的話就不要刪除
* `-x` 顯示出來執行的命令，其實就是和`-n`的結果類似，只是這個會執行
* `-ccflags 'arg list'` 傳遞參數給 5c, 6c, 8c 呼叫
* `-compiler name` 指定相應的編譯器，gccgo 還是 gc
* `-gccgoflags 'arg list'` 傳遞參數給 gccgo 編譯連線呼叫
* `-gcflags 'arg list'` 傳遞參數給 5g, 6g, 8g 呼叫
* `-installsuffix suffix` 為了和預設的安裝套件區別開來，採用這個字首來重新安裝那些依賴的套件，`-race`的時候預設已經是`-installsuffix race`，大家可以透過`-n`命令來驗證
* `-ldflags 'flag list'` 傳遞參數給 5l, 6l, 8l 呼叫
* `-tags 'tag list'` 設定在編譯的時候可以適配的那些 tag，詳細的 tag 限制參考裡面的 [Build Constraints](http://golang.org/pkg/go/build/)

## go clean

這個命令是用來移除當前原始碼套件和關聯原始碼套件裡面編譯產生的檔案。這些檔案包括

```
_obj/            舊的 object 目錄，由 Makefiles 遺留
_test/           舊的 test 目錄，由 Makefiles 遺留
_testmain.go     舊的 gotest 檔案，由 Makefiles 遺留
test.out         舊的 test 記錄，由 Makefiles 遺留
build.out        舊的 test 記錄，由 Makefiles 遺留
*.[568ao]        object 檔案，由 Makefiles 遺留

DIR(.exe)        由 go build 產生
DIR.test(.exe)   由 go test -c 產生
MAINFILE(.exe)   由 go build MAINFILE.go 產生
*.so             由 SWIG 產生
```

我一般都是利用這個命令清除編譯檔案，然後 github 提交原始碼，在本機測試的時候這些編譯檔案都是和系統相關的，但是對於原始碼管理來說沒必要。

```
$ go clean -i -n
cd /Users/astaxie/develop/gopath/src/mathapp
rm -f mathapp mathapp.exe mathapp.test mathapp.test.exe app app.exe
rm -f /Users/astaxie/develop/gopath/bin/mathapp
```

參數介紹

* `-i` 清除關聯的安裝的套件和可執行檔案，也就是透過 go install 安裝的檔案
* `-n` 把需要執行的清除命令顯示出來，但是不執行，這樣就可以很容易的知道底層是如何執行的
* `-r` 迴圈的清除在 import 中引入的套件
* `-x` 顯示出來執行的詳細命令，其實就是`-n`列印的執行版本

## go fmt

有過 C/C++經驗的讀者會知道，一些人經常為程式碼採取 K\&R 風格還是 ANSI 風格而爭論不休。在 go 中，程式碼則有標準的風格。由於之前已經有的一些習慣或其它的原因我們常將程式碼寫成 ANSI 風格或者其它更合適自己的格式，這將為人們在閱讀別人的程式碼時新增不必要的負擔，所以 go 強制了程式碼格式（比如左大括號必須放在行尾），不按照此格式的程式碼將不能編譯透過，為了減少浪費在排版上的時間，go 工具集中提供了一個`go fmt`命令 它可以幫你格式化你寫好的程式碼檔案，使你寫程式碼的時候不需要關心格式，你只需要在寫完之後執行`go fmt <檔名>.go`，你的程式碼就被修改成了標準格式，但是我平常很少用到這個命令，因為開發工具裡面一般都帶了儲存時候自動格式化功能，這個功能其實在底層就是呼叫了`go fmt`。接下來的一節我將講述兩個工具，這兩個工具都自帶了儲存檔案時自動化`go fmt`功能。

使用 go fmt 命令，其實是呼叫了 gofmt，而且需要參數-w，否則格式化結果不會寫入檔案。gofmt -w -l src，可以格式化整個專案。

所以 go fmt 是 gofmt 的上層一個封裝命令，如果想要更多的自訂格式化，可以參考 [gofmt](http://golang.org/cmd/gofmt/)

gofmt 的參數介紹

* `-l` 顯示那些需要格式化的檔案
* `-w` 把改寫後的內容直接寫入到檔案中，而不是作為結果列印到標準輸出。
* `-r` 新增形如“a\[b:len(a)] -> a\[b:]”的重寫規則，方便我們做批量替換
* `-s` 簡化檔案中的程式碼
* `-d` 顯示格式化前後的 diff 而不是寫入檔案，預設是 false
* `-e` 列印所有的語法錯誤到標準輸出。如果不使用此標記，則只會列印不同行的前 10 個錯誤。
* `-cpuprofile` 支援除錯模式，寫入相應的 cpufile 到指定的檔案

## go get

這個命令是用來動態取得遠端程式碼套件的，目前支援的有 BitBucket、GitHub、Google Code 和 Launchpad。這個命令在內部實際上分成了兩步操作：第一步是下載原始碼套件，第二步是執行`go install`。下載原始碼套件的 go 工具會自動根據不同的域名呼叫不同的原始碼工具，對應關係如下：

```
BitBucket (Mercurial Git)
GitHub (Git)
Google Code Project Hosting (Git, Mercurial, Subversion)
Launchpad (Bazaar)
```

所以為了`go get` 能正常工作，你必須確保安裝了合適的原始碼管理工具，並同時把這些命令加入你的 PATH 中。其實`go get`支援自訂域名的功能，具體參見`go help remote`。

參數介紹：

* `-d` 只下載不安裝
* `-f` 只有在你包含了`-u`參數的時候才有效，不讓`-u`去驗證 import 中的每一個都已經取得了，這對於本地 fork 的套件特別有用
* `-fix` 在取得原始碼之後先執行 fix，然後再去做其他的事情
* `-t` 同時也下載需要為執行測試所需要的套件
* `-u` 強制使用網路去更新套件和它的相依套件
* `-v` 顯示執行的命令

## go install

這個命令在內部實際上分成了兩步操作：第一步是產生結果檔案(可執行檔案或者.a 套件)，第二步會把編譯好的結果移到`$GOPATH/pkg`或者`$GOPATH/bin`。

參數支援`go build`的編譯參數。大家只要記住一個參數`-v`就好了，這個隨時隨地的可以檢視底層的執行資訊。

## go test

執行這個命令，會自動讀取原始碼目錄下面名為`*_test.go`的檔案，產生並執行測試用的可執行檔案。輸出的資訊類似

```
ok   archive/tar   0.011s
FAIL archive/zip   0.022s
ok   compress/gzip 0.033s
...
```

預設的情況下，不需要任何的參數，它會自動把你原始碼套件下面所有 test 檔案測試完畢，當然你也可以帶上參數，詳情請參考`go help testflag`

這裡我介紹幾個我們常用的參數：

* `-bench regexp` 執行相應的 benchmarks，例如 `-bench=.`
* `-cover` 開啟測試覆蓋率
* `-run regexp` 只執行 regexp 匹配的函式，例如 `-run=Array` 那麼就執行包含有 Array 開頭的函式
* `-v` 顯示測試的詳細命令

## go tool

`go tool`下面下載聚集了很多命令，這裡我們只介紹兩個，fix 和 vet

* `go tool fix .` 用來修復以前老版本的程式碼到新版本，例如 go1 之前老版本的程式碼轉化到 go1，例如 API 的變化
* `go tool vet directory|files` 用來分析當前目錄的程式碼是否都是正確的程式碼，例如是不是呼叫 fmt.Printf 裡面的參數不正確，例如函式裡面提前 return 了然後出現了無用程式碼之類別的。

## go generate

這個命令是從 Go1.4 開始才設計的，用於在編譯前自動化產生某類別程式碼。`go generate`和`go build`是完全不一樣的命令，透過分析原始碼中特殊的註釋，然後執行相應的命令。這些命令都是很明確的，沒有任何的依賴在裡面。而且大家在用這個之前心裡面一定要有一個觀念，這個 `go generate` 是給你用的，不是給使用你這個套件的人用的，主要是方便你自動產生一些程式碼。

這裡我們來舉一個簡單的例子，例如我們經常會使用 `yacc` 來產生程式碼，那麼我們常用這樣的命令：

```
go tool yacc -o gopher.go -p parser gopher.y
```

-o 指定了輸出的檔名， -p 指定了 package 的名稱，這是一個單獨的命令，如果我們想讓`go generate`來觸發這個命令，那麼就可以在當前目錄的任意一個`xxx.go`檔案裡面的任意位置增加一行如下的註釋：

```
//go:generate go tool yacc -o gopher.go -p parser gopher.y
```

這裡我們注意了，`//go:generate`是沒有任何空格的，這其實就是一個固定的格式，在掃描原始碼檔案的時候就是根據這個來判斷的。

所以我們可以透過如下的命令來產生，編譯，測試。如果`gopher.y`檔案有修改，那麼就重新執行`go generate`重新產生檔案就好。

```
$ go generate
$ go build
$ go test
```

## godoc

在 Go1.2 版本之前還支援`go doc`命令，但是之後全部移到了 godoc 這個命令下，需要這樣安裝`go get golang.org/x/tools/cmd/godoc`

很多人說 go 不需要任何的第三方文件，例如 chm 手冊之類別的（其實我已經做了一個了，[chm 手冊](https://github.com/astaxie/godoc)），因為它內部就有一個很強大的文件工具。

如何檢視相應 package 的文件呢？ 例如 builtin 套件，那麼執行`godoc builtin` 如果是 http 套件，那麼執行`godoc net/http` 檢視某一個套件裡面的函式，那麼執行`godoc fmt Printf` 也可以檢視相應的程式碼，執行`godoc -src fmt Printf`

透過命令在命令列執行 godoc -http=:埠號 比如`godoc -http=:8080`。然後在瀏覽器中開啟`127.0.0.1:8080`，你將會看到一個 golang.org 的本地 copy 版本，透過它你可以查詢 pkg 文件等其它內容。如果你設定了 GOPATH，在 pkg 分類下，不但會列出標準套件的文件，還會列出你本地 `GOPATH` 中所有專案的相關文件，這對於經常被牆的使用者來說是一個不錯的選擇。

## 其它命令

go 還提供了其它很多的工具，例如下面的這些工具

```
go version 檢視 go 當前的版本
go env 檢視當前 go 的環境變數
go list 列出當前全部安裝的 package

go run 編譯並執行 Go 程式
```

以上這些工具還有很多參數沒有一一介紹，使用者可以使用`go help 命令` 取得更詳細的幫助資訊。

## links

* [目錄](https://github.com/doggy8088/build-web-application-with-golang-zhtw/tree/4cbbaa31bdfc3678915eb91f23db2f1bad554a20/preface.md)
* 上一節: [GOPATH 與工作空間](https://willh.gitbook.io/build-web-application-with-golang-zhtw/01.0/01.2)
* 下一節: [Go 開發工具](https://willh.gitbook.io/build-web-application-with-golang-zhtw/01.0/01.4)
