我在這邊使用的是 Windows 中的 Go 1.13 版本,你可以至 Go 的官方網站 下載安裝 Go。
如果想來點不同的安裝方式,可以參考〈門外漢的 Go 輕量開發環境〉,在 Raspberry Pi 上的 Docker 容器中建立相關環境,就目前為止。
你至少得設定 GOROOT
環境變數,這會是你的 Go 安裝目錄。
go run
要撰寫第一個 Hello, World 程式,你可以建立一個 main.go,在當中撰寫以下的內容:
package main
import "fmt"
func main() {
fmt.Println("Hello, World")
fmt.Println("哈囉!世界!")
}
每個 .go 原始碼,都必須從 package
定義開始,而對於包括程式進入點 main
函式的 .go 原始碼,必須是在 package main
之中,為了要能輸出訊息,這邊使用了 fmt
套件(package)之中的 Println
函式,開頭的大寫 P 表示這是個公開的函式,可以在套件之外進行呼叫。
Go 的創建者之一也是 UTF-8 的創建者,因此,Go 可以直接處理多國語言,只要你確定編輯器編碼為 UTF-8 就可以了,如果你使用 vim,可以在 vim 的命令模式下輸入 :set encoding=utf-8
,或者是在 .vimrc 之中增加一行 set encoding=utf-8
。
Go 可以用直譯的方式來執行程式,第一個 Hello, World 程式就是這麼做的,執行 go run
指定你的原始碼檔名就可以了:
$ go run main.go
Hello, World
哈囉!世界!
package 與 GOPATH
那麼,一開始的 package
是怎麼回事?試著先來建立一個 hello.go:
package hello
import "fmt"
func HelloWorld() {
fmt.Println("Hello, World")
}
記得,package
中定義的函式,名稱必須是以大寫開頭,其他套件外的程式,才能進行呼叫,若函式名稱是小寫,那麼會是套件中才可以使用的函式。
接著,原本的 main.go 修改為:
package main
import "hello"
func main() {
hello.HelloWorld()
}
現在顯然地,main.go 中要用到方才建立的 hello
套件中的 HelloWorld
函式,這時 package
的設定就會發揮一下效用,你得將 hello.go 移到 src/hello 目錄之中,也就是目錄名稱必須符合 package
設定之名稱。
同樣地,你可以將 main.go 移到 src/main 目錄之中,以符合 package
的設定。
而 src 的位置,必須是在 GOROOT
或者是 GOPATH
的路徑中可以找到,當 Go 需要某套件中的元素時,會分別到這兩個環境變數的目錄之中,查看 src 中是否有相應於套件的原始碼存在。
為了方便,通常會設定 GOPATH
,例如,指向目前的工作目錄:
set GOPATH=c:\workspace\go-exercise
如果沒有設定 GOPATH
的話,Go 預設會是使用者目錄的 go 目錄,雖然目前 GOPATH
中只一個目錄,不過 GOPATH
中可以設定數個目錄,現在我的 go-exercise 目錄底下會有這些東西:
go-exercise
└─src
├─hello
│ hello.go
│
└─main
main.go
接著在 go 目錄中執行指令 go run src/main/main.go
的話,你就會看到 Hello, World 了。
go build
如果想編譯原始碼為可執行檔,那麼可以使用 go build
,例如,直接在 go 目錄中執行 go build src/main/main.go
,就會在執行指令的目錄下,產生一個名稱為 main.exe 的可執行檔,可執行檔的名稱是來自己指定的原始碼檔案主檔名,執行產生出來的可執行檔就會顯示 Hello, World。
你也可以建立一個 bin 目錄,然後執行 go build -o bin/main.exe src/main/main.go
,這樣產生出來的可執行檔,就會被放在 bin 底下。
go install
每次使用 go build
,都是從原始碼編譯為可執行檔,這比較沒有效率,如果想要編譯時更有效率一些,可以使用 go install
,例如,在目前既有的目錄與原始碼架構之下,於 go 目錄中執行 go install hello
的話,你就會發現有以下的內容:
go-exercise
├─bin
│ main.exe
│
├─pkg
│ └─windows_amd64
│ hello.a
│
└─src
├─hello
│ hello.go
│
└─main
main.go
go install packageName
表示要安裝指定名稱的套件,如果是 main
套件,那麼會在 bin 中產生可執行檔,如果是公用套件,那麼會在 pkg 目錄的 $GOOS
_$GOARCH
目錄中產生 .a 檔案,你可以使用 go env
來查看 Go 使用到的環境變數,例如:
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Justin\AppData\Local\go-build
set GOENV=C:\Users\Justin\AppData\Roaming\go\env
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\Justin\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:\Winware\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=C:\Winware\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\Justin\AppData\Local\Temp\go-build282125542=/tmp/go-build -gno-record-gcc-switches
.a 檔案是編譯過後的套件,因此,你看到的 hello.a,就是 hello.go 編譯之後的結果,如果編譯時需要某個套件,而對應的 .a 檔案存在,且原始碼自上次編譯後未曾經過修改,那麼就會直接使用 .a 檔案,而不是從原始碼開始編譯起。
os.Args
那麼,如果想在執行 Go 程式時使用命令列引數呢?可以使用 os
套件的 Args
,例如,寫一個 main.go:
package main
import "os"
import "fmt"
func main() {
fmt.Printf("Command: %s\n", os.Args[0])
fmt.Printf("Hello, %s\n", os.Args[1])
}
os.Args
是個陣列,索引從 0 開始,索引 0 會是編譯後的可執行檔名稱,索引 1 開始會是你提供的引數,例如,在執行過 go build 或 go install 之後,如下直接執行編譯出來的執行檔,會產生的訊息是…
$ ./bin/main Justin
Command: ./bin/main
Hello, Justin
go doc
fmt
的 Printf,就像是 C 的 printf
,可用的格式控制可參考 Package fmt 的說明。實際上,Go 本身附帶了說明文件,可以執行 go doc <pkg> <sym>[.<method>]
來查詢說明。例如:
$ go doc fmt.Printf
func Printf(format string, a ...interface{}) (n int, err error)
Printf formats according to a format specifier and writes to standard
output. It returns the number of bytes written and any write error
encountered.