go doc
命令可以打印附于Go語言程序?qū)嶓w上的文檔。我們可以通過把程序?qū)嶓w的標(biāo)識(shí)符作為該命令的參數(shù)來達(dá)到查看其文檔的目的。
插播:所謂Go語言的程序?qū)嶓w,是指變量、常量、函數(shù)、結(jié)構(gòu)體以及接口。而程序?qū)嶓w的標(biāo)識(shí)符即是代表它們的名稱。標(biāo)識(shí)符又分非限定標(biāo)識(shí)符和限定標(biāo)識(shí)符。其中,限定標(biāo)識(shí)符一般用于表示某個(gè)代碼包中的程序?qū)嶓w或者某個(gè)結(jié)構(gòu)體類型中的方法或字段。例如,標(biāo)準(zhǔn)庫代碼包io
中的名為EOF
的變量用限定標(biāo)識(shí)符表示即io.EOF
。又例如,如果我有一個(gè)sync.WaitGroup
類型的變量wg
并且想調(diào)用它的Add
方法,那么可以這樣寫wg.Add()
。其中,wg.Add
就是一個(gè)限定標(biāo)識(shí)符,而后面的()
則代表了調(diào)用操作。
下面說明怎樣使用go doc
命令。先來看一下go doc
命令課結(jié)束的標(biāo)記。
表0-5 go doc
命令的標(biāo)記說明
標(biāo)記名稱 | 標(biāo)記描述 |
---|---|
-c | 加入此標(biāo)記后會(huì)使go doc 命令區(qū)分參數(shù)中字母的大小寫。默認(rèn)情況下,命令是大小寫不敏感的。 |
-cmd | 加入此標(biāo)記后會(huì)使go doc 命令同時(shí)打印出main 包中的可導(dǎo)出的程序?qū)嶓w(其名稱的首字母大寫)的文檔。默認(rèn)情況下,這部分文檔是不會(huì)被打印出來的。 |
-u | 加入此標(biāo)記后會(huì)使go doc 命令同時(shí)打印出不可導(dǎo)出的程序?qū)嶓w(其名稱的首字母小寫)的文檔。默認(rèn)情況下,這部分文檔是不會(huì)被打印出來的。 |
這幾個(gè)標(biāo)記的意圖都非常簡(jiǎn)單和明確,大家可以根據(jù)實(shí)際情況選用。
go doc
命令可以后跟一個(gè)或兩個(gè)參數(shù)。當(dāng)然,我們也可以不附加任務(wù)參數(shù)。如果不附加參數(shù),那么go doc
命令會(huì)試圖打印出當(dāng)前目錄所代表的代碼包的文檔及其中的包級(jí)程序?qū)嶓w的列表。
例如,我要在goc2p項(xiàng)目的loadgen
代碼包所在目錄中運(yùn)行go doc
命令的話,那么就會(huì)是這樣:
hc@ubt:~/golang/goc2p/src/loadgen$ go doc
package loadgen // import "loadgen"
func NewGenerator(
caller lib.Caller,
timeoutNs time.Duration,
lps uint32,
durationNs time.Duration,
resultCh chan *lib.CallResult) (lib.Generator, error)
如果你需要指定代碼包或程序?qū)嶓w,那么就需要在go doc
命令后附上參數(shù)了。例如,只要我本地的goc2p項(xiàng)目的所在目錄存在于GOPATH環(huán)境變量中,我就可以在任意目錄中敲入go doc loadgen
。如此得到的輸出一定是與上面那個(gè)示例一致的。
看過loadgen
代碼包中源碼的讀者會(huì)知道,其中只有一個(gè)可導(dǎo)出的程序?qū)嶓w,即NewGenerator
函數(shù)。這也是上述示例中如此輸出的原因。該代碼包中的結(jié)構(gòu)體類型myGenerator
是不可導(dǎo)出,但是我們只需附加-u
標(biāo)記便可查看它的文檔了:
hc@ubt:~$ go doc -u loadgen.myGenerator
type myGenerator struct {
caller lib.Caller // 調(diào)用器。
timeoutNs time.Duration // 處理超時(shí)時(shí)間,單位:納秒。
lps uint32 // 每秒載荷量。
durationNs time.Duration // 負(fù)載持續(xù)時(shí)間,單位:納秒。
concurrency uint32 // 并發(fā)量。
tickets lib.GoTickets // Goroutine票池。
stopSign chan byte // 停止信號(hào)的傳遞通道。
cancelSign byte // 取消發(fā)送后續(xù)結(jié)果的信號(hào)。
endSign chan uint64 // 完結(jié)信號(hào)的傳遞通道,同時(shí)被用于傳遞調(diào)用執(zhí)行計(jì)數(shù)。
callCount uint64 // 調(diào)用執(zhí)行計(jì)數(shù)。
status lib.GenStatus // 狀態(tài)。
resultCh chan *lib.CallResult // 調(diào)用結(jié)果通道。
}
載荷發(fā)生器的實(shí)現(xiàn)。
func (gen *myGenerator) Start()
func (gen *myGenerator) Status() lib.GenStatus
func (gen *myGenerator) Stop() (uint64, bool)
func (gen *myGenerator) asyncCall()
func (gen *myGenerator) genLoad(throttle <-chan time.Time)
func (gen *myGenerator) handleStopSign(callCount uint64)
func (gen *myGenerator) init() error
func (gen *myGenerator) interact(rawReq *lib.RawReq) *lib.RawResp
func (gen *myGenerator) sendResult(result *lib.CallResult) bool
如此一來,loadgen.myGenerator
類型的文檔、字段和方法都盡收眼底。注意,這里我們使用到了限定標(biāo)識(shí)符。下面再進(jìn)一步,如果你只想查看loadgen.myGenerator
類型的init
方法的文檔,那么只要續(xù)寫這個(gè)限定標(biāo)識(shí)符就可以了,像這樣:
hc@ubt:~$ go doc -u loadgen.myGenerator.init
func (gen *myGenerator) init() error
初始化載荷發(fā)生器。
注意,結(jié)構(gòu)體類型中的字段的文檔是無法被單獨(dú)打印的。另外,go doc
命令根據(jù)參數(shù)查找代碼包或程序?qū)嶓w的順序是:先Go語言根目錄(即GOROOT所環(huán)境變量指定的那個(gè)目錄)后工作區(qū)目錄(即GOPATH環(huán)境變量包含的那些目錄)。并且,在前者或后者中,go doc
命令的查找順序遵循字典序。因此,如果某個(gè)工作區(qū)目錄中的代碼包與標(biāo)準(zhǔn)庫中的包重名了,那么它是無法被打印出來的。go doc
命令只會(huì)打印出第一個(gè)匹配的代碼包或程序?qū)嶓w的文檔。
我們?cè)谇懊嬲f過,go doc
命令還可以接受兩個(gè)參數(shù)。這是一種更加精細(xì)的指定代碼包或程序?qū)嶓w的方式。一個(gè)顯著的區(qū)別是,如果你想打印標(biāo)準(zhǔn)庫代碼包net/http
中的結(jié)構(gòu)體類型Request
的文檔,那么可以這樣敲入go doc
命令:
go doc http.Request
注意,這里并沒有寫入net/http
代碼包的導(dǎo)入路徑,而只是寫入了其中的最后一個(gè)元素http
。但是如果你把http.Request
拆成兩個(gè)參數(shù)(即http Request
)的話,命令程序就會(huì)什么也查不到了。因?yàn)檫@與前一種用法的解析方式是不一樣的。正確的做法是,當(dāng)你指定兩個(gè)參數(shù)時(shí),作為第一個(gè)參數(shù)的代碼包名稱必須是完整的導(dǎo)入路徑,即:在敲入命令go doc net/http Request
后,你會(huì)得到想要的結(jié)果。
最后,在給定兩個(gè)參數(shù)時(shí),go doc
會(huì)打印出所有匹配的文檔,而不是像給定一個(gè)參數(shù)時(shí)那樣只打印出第一個(gè)匹配的文檔。這對(duì)于查找只有大小寫不同的多個(gè)方法(如New
和new
)的文檔來說非常有用。
命令godoc
是一個(gè)很強(qiáng)大的工具,同樣用于展示指定代碼包的文檔。在Go語言的1.5版本中,它是一個(gè)內(nèi)置的標(biāo)準(zhǔn)命令。
該命令有兩種模式可供選擇。如果在執(zhí)行命令時(shí)不加入-http
標(biāo)記,則該命令就以命令行模式運(yùn)行。在打印純文本格式的文檔到標(biāo)準(zhǔn)輸出后,命令執(zhí)行就結(jié)束了。比如,我們用命令行模式查看代碼包fmt的文檔:
hc@ubt:~$ godoc fmt
為了節(jié)省篇幅,我們?cè)谶@里略去了文檔查詢結(jié)果。讀者可以自己運(yùn)行一下上述命令。在該命令被執(zhí)行之后,我們就可以看到編排整齊有序的文檔內(nèi)容了。這包括代碼包fmt
及其中所有可導(dǎo)出的包級(jí)程序?qū)嶓w的聲明、文檔和例子。
有時(shí)候我們只是想查看某一個(gè)函數(shù)或者結(jié)構(gòu)體類型的文檔,那么我們可以將這個(gè)函數(shù)或者結(jié)構(gòu)體的名稱加入命令的后面,像這樣:
hc@ubt:~$ godoc fmt Printf
或者:
hc@ubt:~$ godoc os File
如果我們想同時(shí)查看一個(gè)代碼包中的幾個(gè)函數(shù)的文檔,則僅需將函數(shù)或者結(jié)構(gòu)體名稱追加到命令后面。比如我們要查看代碼包fmt
中函數(shù)Printf
和函數(shù)Println
的文檔:
hc@ubt:~$ godoc fmt Printf Println
如果我們不但想在文檔中查看可導(dǎo)出的程序?qū)嶓w的聲明,還想看到它們的源碼,那么我們可以在執(zhí)行godoc
命令的時(shí)候加入標(biāo)記-src
,比如這樣:
hc@ubt:~$ godoc -src fmt Printf
Go語言為程序使用示例代碼設(shè)立了專有的規(guī)則。我們?cè)谶@里暫不討論這個(gè)規(guī)則的細(xì)節(jié)。只需要知道正因?yàn)橛辛诉@個(gè)專有規(guī)則,使得godoc
命令可以根據(jù)這些規(guī)則提取相應(yīng)的示例代碼并把它們加入到對(duì)應(yīng)的文檔中。如果我們想在查看代碼包net
中的結(jié)構(gòu)體類型Listener
的文檔的同時(shí)查看關(guān)于它的示例代碼,那么我們只需要在執(zhí)行命令時(shí)加入標(biāo)記-ex
。使用方法如下:
hc@ubt:~$ godoc -ex net/http FileServer
注意,我們?cè)谑褂?code>godoc命令時(shí),只能把代碼包和程序?qū)嶓w的標(biāo)識(shí)符拆成兩個(gè)參數(shù)。也就是說,godoc
命令不支持前文所述的go doc
命令的單參數(shù)用法。
在實(shí)際的Go語言環(huán)境中,我們可能會(huì)遇到一個(gè)命令源碼文件所產(chǎn)生的可執(zhí)行文件與代碼包重名的情況。比如,這里介紹的標(biāo)準(zhǔn)命令go
和官方代碼包go
?,F(xiàn)在我們要明確的告訴godoc
命令要查看可執(zhí)行文件go的文檔,我們需要在名稱前加入cmd/
前綴:
hc@ubt:~$ godoc cmd/go
另外,如果我們想查看HTML格式的文檔,就需要加入標(biāo)記-html
。當(dāng)然,這樣在命令行模式下的查看效果是很差的。但是,如果仔細(xì)查看的話,可以在其中找到一些相應(yīng)源碼的鏈接地址。
一般情況下,godoc
命令會(huì)去Go語言根目錄和環(huán)境變量GOPATH包含的工作區(qū)目錄中查找代碼包。我們可以通過加入標(biāo)記-goroot
來制定一個(gè)Go語言根目錄。這個(gè)被指定的Go語言根目錄僅被用于當(dāng)次命令的執(zhí)行。示例如下:
hc@ubt:~$ godoc -goroot="/usr/local/go" fmt
現(xiàn)在讓我們來看看另外一種模式。如果我們?cè)趫?zhí)行命令時(shí)加上-http
標(biāo)記則會(huì)啟用另一模式。這種模式被叫做Web服務(wù)器模式,它以Web頁面的形式提供Go語言文檔。
我們使用如下命令啟動(dòng)這個(gè)文檔Web服務(wù)器:
hc@ubt:~/golang/goc2p$ godoc -http=:6060
標(biāo)記-http
的值:6060
表示啟動(dòng)的Web服務(wù)器使用本機(jī)的6060端口。之后,我們就可以通過在網(wǎng)絡(luò)瀏覽器的地址欄中輸入http://localhost:6060來查看以網(wǎng)頁方式展現(xiàn)的Go文檔了。
http://wiki.jikexueyuan.com/project/go-command-tutorial/images/0-1.png" alt="本機(jī)的Go文檔Web服務(wù)首頁" />
圖0-1 本機(jī)的Go文檔Web服務(wù)首頁
這與Go語言官方站點(diǎn)的Web服務(wù)頁面如出一轍。這使得我們?cè)诓环奖阍L問Go語言官方站點(diǎn)的情況下也可以查看Go語言文檔。并且,更便利的是,通過本機(jī)的Go文檔Web服務(wù),我們還可以查看所有本機(jī)工作區(qū)下的代碼的文檔。比如,goc2p項(xiàng)目中的代碼包pkgtool
的頁面如下圖:
http://wiki.jikexueyuan.com/project/go-command-tutorial/images/0-2.png" alt="goc2p項(xiàng)目中的pkgtool包的Go文檔頁面" />
圖0-2 goc2p項(xiàng)目中的pkgtool包的Go文檔頁面
現(xiàn)在,我們?cè)诒緳C(jī)開啟Go文檔Web服務(wù)器,端口為9090。命令如下:
hc@ubt:~$ godoc -http=:9090 -index
注意,要使用-index
標(biāo)記開啟搜索索引。這個(gè)索引會(huì)在服務(wù)器啟動(dòng)時(shí)創(chuàng)建并維護(hù)。如果不加入此標(biāo)記,那么無論在Web頁面還是命令行終端中都是無法進(jìn)行查詢操作的。
索引中提供了標(biāo)識(shí)符和全文本搜索信息(通過正則表達(dá)式為可搜索性提供支持)。全文本搜索結(jié)果顯示條目的最大數(shù)量可以通過標(biāo)記-maxresults
提供。標(biāo)記-maxresults
默認(rèn)值是10000。如果不想提供如此多的結(jié)果條目,可以設(shè)置小一些的值。甚至,如果不想提供全文本搜索結(jié)果,可以將標(biāo)記-maxresults
的值設(shè)置為0,這樣服務(wù)器就只會(huì)創(chuàng)建標(biāo)識(shí)符索引,而根本不會(huì)創(chuàng)建全文本搜索索引了。標(biāo)識(shí)符索引即為對(duì)程序?qū)嶓w名稱的索引。
正因?yàn)樵谑褂昧?code>-index標(biāo)記的情況下文檔服務(wù)器會(huì)在啟動(dòng)時(shí)創(chuàng)建索引,所以在文檔服務(wù)器啟動(dòng)之后還不能立即提供搜索服務(wù),需要稍等片刻。在索引為被創(chuàng)建完畢之前,我們的搜索操作都會(huì)得到提示信息“Indexing in progress: result may be inaccurate”。
如果我們?cè)诒緳C(jī)用godoc
命令啟動(dòng)了Go文檔Web服務(wù)器,且IP地址為192.168.1.4、端口為9090,那么我們就可以在另一個(gè)命令行終端甚至另一臺(tái)能夠與本機(jī)聯(lián)通的計(jì)算機(jī)中通過如下命令進(jìn)行查詢了。查詢命令如下:
hc@ubt:~$ godoc -q -server="192.168.1.4:9090" Listener
命令的最后為要查詢的內(nèi)容,可以是任何你想搜索的字符串,而不僅限于代碼包、函數(shù)或者結(jié)構(gòu)體的名稱。
標(biāo)記-q
開啟了遠(yuǎn)程查詢的功能。而標(biāo)記-server="192.168.1.4:9090"
則指明了遠(yuǎn)程文檔服務(wù)器的IP地址和端口號(hào)。實(shí)際上,如果不指明遠(yuǎn)程查詢服務(wù)器的地址,那么該命令會(huì)自行將地址“:6060”和“golang.org”作為遠(yuǎn)程查詢服務(wù)器的地址。這兩個(gè)地址即是默認(rèn)的本機(jī)文檔Web站點(diǎn)地址和官方的文檔Web站點(diǎn)地址。所以執(zhí)行如下命令我們也可以查詢到標(biāo)準(zhǔn)庫的信息:
hc@ubt:~$ godoc -q fmt
命令godoc
還有很多可用的標(biāo)記,但在通常情況下并不常用。讀者如果有興趣,可以在命令行環(huán)境下敲入godoc
并查看其文檔。
至于怎樣才能寫出優(yōu)秀的代碼包文檔,我在《Go并發(fā)編程實(shí)戰(zhàn)》的5.2節(jié)中做了詳細(xì)說明。