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