在《Go并發(fā)編程實戰(zhàn)》的第二章中,我介紹了Go源碼文件的分類。Go源碼文件包括:命令源碼文件、庫源碼文件和測試源碼文件。其中,命令源碼文件總應該屬于main
代碼包,且在其中有無參數(shù)聲明、無結(jié)果聲明的main函數(shù)。單個命令源碼文件可以被單獨編譯,也可以被單獨安裝(可能需要設置環(huán)境變量GOBIN)。當然,命令源碼文件也可以被單獨運行。我們想要運行命令源碼文件就需要使用命令go run
。
go run
命令可以編譯并運行命令源碼文件。由于它其中包含了編譯動作,因此它也可以接受所有可用于go build
命令的標記。除了標記之外,go run
命令只接受Go源碼文件作為參數(shù),而不接受代碼包。與go build
命令和go install
命令一樣,go run
命令也不允許多個命令源碼文件作為參數(shù),即使它們在同一個代碼包中也是如此。而原因也是一致的,多個命令源碼文件會都有main函數(shù)聲明。
如果命令源碼文件可以接受參數(shù),那么在使用go run
命令運行它的時候就可以把它的參數(shù)放在它的文件名后面,像這樣:
hc@ubt:~/golang/goc2p/src/helper/ds$ go run showds.go -p ~/golang/goc2p
在上面的示例中,我們使用go run
命令運行命令源碼文件showds.go。這個命令源碼文件可以接受一個名稱為“p”的參數(shù)。我們用“-p”這種形式表示“p”是一個參數(shù)名而不是參數(shù)值。它與源碼文件名之間需要用空格隔開。參數(shù)值會放在參數(shù)名的后面,兩者成對出現(xiàn)。它們之間也要用空格隔開。如果有第二個參數(shù),那么第二個參數(shù)的參數(shù)名與第一個參數(shù)的參數(shù)值之間也要有一個空格。以此類推。
go run
命令只能接受一個命令源碼文件以及若干個庫源碼文件(必須同屬于main
包)作為文件參數(shù),且不能接受測試源碼文件。它在執(zhí)行時會檢查源碼文件的類型。如果參數(shù)中有多個或者沒有命令源碼文件,那么go run
命令就只會打印錯誤提示信息并退出,而不會繼續(xù)執(zhí)行。
在通過參數(shù)檢查后,go run
命令會將編譯參數(shù)中的命令源碼文件,并把編譯后的可執(zhí)行文件存放到臨時工作目錄中。
編譯和運行過程
為了更直觀的體現(xiàn)出go run
命令中的操作步驟,我們在執(zhí)行命令時加入標記-n
,用于打印相關(guān)命令而不實際執(zhí)行?,F(xiàn)在讓我們來模擬運行g(shù)oc2p項目中的代碼包helper/ds的命令源碼文件showds.go。示例如下:
hc@ubt:~/golang/goc2p/src/helper/ds$ go run -n showds.go
#
# command-line-arguments
#
mkdir -p $WORK/command-line-arguments/_obj/
mkdir -p $WORK/command-line-arguments/_obj/exe/
cd /home/hc/golang/goc2p/src/helper/ds
/usr/local/go1.5/pkg/tool/linux_amd64/compile -o $WORK/command-line-arguments.a -trimpath $WORK -p main -complete -buildid df49387da030ad0d3bebef3f046d4013f8cb08d3 -D _/home/hc/golang/goc2p/src/helper/ds -I $WORK -pack ./showds.go
cd .
/usr/local/go1.5/pkg/tool/linux_amd64/link -o $WORK/command-line-arguments/_obj/exe/showds -L $WORK -w -extld=clang -buildmode=exe -buildid=df49387da030ad0d3bebef3f046d4013f8cb08d3 $WORK/command-line-arguments.a
$WORK/command-line-arguments/_obj/exe/showds
在上面的示例中并沒有顯示針對命令源碼文件showds.go的依賴包進行編譯和運行的相關(guān)打印信息。這是因為該源碼文件的所有依賴包已經(jīng)在之前被編譯過了。
現(xiàn)在,我們來逐行解釋這些被打印出來的信息。
以前綴“#”開始的是注釋信息。我們看到信息中有三行注釋信息,并在中間行出現(xiàn)了內(nèi)容“command-line-arguments”。我們在講go build
命令的時候說過,編譯命令在分析參數(shù)的時候如果發(fā)現(xiàn)第一個參數(shù)是Go源碼文件而不是代碼包時,會在內(nèi)部生成一個名為“command-line-arguments”的虛擬代碼包。所以這里的注釋信息就是要告訴我們下面的幾行信息是關(guān)于虛擬代碼包“command-line-arguments”的。
打印信息中的“$WORK”表示臨時工作目錄的絕對路徑。為了存放對虛擬代碼包“command-line-arguments”的編譯結(jié)果,命令在臨時工作目錄中創(chuàng)建了名為command-line-arguments的子目錄,并在其下又創(chuàng)建了_obj子目錄和_obj/exe子目錄。
然后,命令程序使用Go語言工具目錄compile
命令對命令源碼文件showds.go進行了編譯,并把結(jié)果文件存放到了$WORK目錄下,名為command-line-arguments.a。其中,compile
是Go語言自帶的編程工具。
在編譯成功之后,命令程序使用鏈接命令link
生成最終的可執(zhí)行文件,并將其存于$WORK/command-line-arguments/_obj/exe/目錄中。打印信息中的最后一行表示,命令運行了生成的可執(zhí)行文件。
通過對這些打印出來的命令的解讀,我們了解了臨時工作目錄的用途以和內(nèi)容。
在上面的示例中,我們只是讓go run
命令打印出運行命令源碼文件showds.go過程中需要執(zhí)行的命令,而沒有真正運行它。如果我們想真正運行命令源碼文件showds.go并且想知道臨時工作目錄的位置,就需要去掉標記-n
并且加上標記-work
。當然,如果依然想看到過程中執(zhí)行的命令,可以加上標記-x
。如果讀者已經(jīng)看過之前我們對go build
命令的介紹,就應該知道標記-x
與標記-n
一樣會打印出過程執(zhí)行的命令,但不同的這些命令會被真正的執(zhí)行。調(diào)整這些標記之后的命令就像這樣:
hc@ubt:~/golang/goc2p/src/helper/ds$ go run -x -work showds.go
當命令真正執(zhí)行后,臨時工作目錄中就會出現(xiàn)實實在在的內(nèi)容了,像這樣:
/tmp/go-build604555989:
command-line-arguments/
_obj/
exe/
showds
command-line-arguments.a
由于上述命令中包含了-work
標記,所以我們可以從其輸出中找到實際的工作目錄(這里是/tmp/go-build604555989)。有意思的是,我們恰恰可以通過運行命令源碼文件showds.go來查看這個臨時工作目錄的目錄樹:
hc@ubt:~/golang/goc2p/src/helper/ds$ go run showds.go -p /tmp/go-build604555989
讀者可以自己試一試。
我們在前面介紹過,命令源碼文件如果可以接受參數(shù),則可以在執(zhí)行go run
命令運行這個命令源碼文件時把參數(shù)名和參數(shù)值成對的追加在后面。實際上,如果在命令后追加參數(shù),那么在最后執(zhí)行生成的可執(zhí)行文件的時候也會追加一致的參數(shù)。例如,如果這樣執(zhí)行命令:
hc@ubt:~/golang/goc2p/src/helper/ds$ go run -n showds.go -p ~/golang/goc2p
那么帶-x
或-n
標記的命令程序打印的最后一個命令就是:
$WORK/command-line-arguments/_obj/exe/showds -p /home/hc/golang/goc2p
可見,go run
命令會把追加到命令源碼文件后面的參數(shù)原封不動的傳給對應的可執(zhí)行文件。
以上簡要展示了一個命令源碼文件從編譯到運行的全過程。請記住,go run
命令包含了兩個動作:編譯命令源碼文件和運行對應的可執(zhí)行文件。