前段時間有群友在群裡問乙個go語言的問題:
就是有乙個main.go的main函式裡呼叫了另乙個demo.go裡的hello()函式。其中main.go和hello.go同屬於main包。但是在main.go的目錄下執行go run main.go卻報hello函式沒有定義的錯:
**結構如下:
**gopath ---- src**
**----gohello**
**----hello.go**
** ----main.go**
main.go如下:
package main
import "fmt"
func main()
hello.go如下:
package main
import "fmt"
func hello()
當時我看了以為是他gopath配置的有問題,然後自己也按照這樣試了一下,報同樣的錯,在網上查了,也有兩篇文章是關於這個錯的,也提供了解決方法,即用go run main.go hello.go,試了確實是可以的。
雖然是個很簡單的問題,但是也涉及到了go語言底層對於命令列引數的解析。那就來分析一下語言底層的實現吧,看一下底層做了什麼,為什麼報這個錯?
分析:
以下使用到的go sdk版本為1.8.3
該版本中go支援的基本命令有以下16個:
build compile packages and dependencies
clean remove object files
doc show documentation for package or symbol
env print go environment information
bug start a bug report
fix run go tool fix on packages
fmt run gofmt on package sources
generate generate go files by processing source
get download and install packages and dependencies
install compile and install packages and dependencies
list list packages
run compile and run go program
test test packages
tool run specified go tool
version print go version
vet run go tool vet on packages
在go sdk的src/cmd/go包下有main.go檔案中,command型別的commands陣列對該16個命令提供了支援:
我們首先知道go語言的初始化流程如下:
在執行main.go中的主函式main之前,對import進來的包按順序初始化,最後初始化main.go中的型別和變數,當初始化到commands陣列時,由於cmdrun定義在於main.go同包下的run.go中,那麼就先去初始化run.go中的變數和init方法,如下**,先把cmdrun初始化為command型別,然後執行init()函式。
var cmdrun = &command
func init()
init()中,將runrun(其實型別是乙個方法,用於處理run後的引數)賦值給cmdru.run,addbuildflags(cmdrun)主要是給run後面增加命令列引數(如:-x是列印其執行過程中用到的所有命令,同時執行它們)。其他15個命令和cmdrun類似,各有各的run實現。
下來主要看main.go中main的這塊**:
for _, cmd := range commands
if cmd.customflags else
cmd.run(cmd, args)
exit()
return}}
這塊**遍歷commands陣列,當遍歷到cmdrun時,cmd.name()其實就是拿到cmdrun.usageline的第乙個單詞run
就會進入if分支,由於cmd.customflags沒有初始化故為false,走else分支,然後開始解析args命令列引數,args[1:]即取run後的所有引數。然後去執行cmd.run(cmd, args),對於cmdrun來說,這裡執行的就是run.go中init()的第一行賦值cmdrun.run(上面說了,這是乙個函式,不同命令實現方式不同),即去執行run.go中的runrun函式,該函式主要是將命令列引數當檔案去處理,如果是_test為字尾的,即測試檔案,直接報錯。如果是目錄也直接報錯(而且go run後面只能包含乙個含main函式的go檔案)。注意到有這麼一行:
p := gofilespackage(files)
gofilespackage(files)除了校驗檔案型別和字尾,還會入棧,載入,出棧等操作,由於啟動的時候沒有傳遞hello.go,所以系統載入main.go時找不到hello函式,導致報錯。 golang初探與命令原始碼分析
前段時間有群友在群裡問乙個go語言的問題 就是有乙個main.go的main函式裡呼叫了另乙個demo.go裡的hello 函式。其中main.go和hello.go同屬於main包。但是在main.go的目錄下執行go run main.go卻報hello函式沒有定義的錯 結構如下 gopath ...
OkHttp原始碼初探
在之前的文章我中我們介紹了okhttp的基本使用方法並簡單說明了原始碼下各個module的功能作用,從這篇開始我們將要開始分析okhttp的原始碼。首先,我們先來回憶一下okhttp的使用過程 1.建立乙個okhttpclient物件 2.建立乙個request物件 3.呼叫okhttpclient...
RequireJS原始碼初探
前兩天跟著葉小釵的部落格,看了下requirejs的原始碼,大體了解了其中的執行過程。不過在何時進行依賴項的載入,以及具體的 在何處執行,還沒有搞透徹,奈何能力不夠,只能先記錄一下了。看原始碼從頭開始看,肯定是不切實際的。按照葉小釵的方法,是從data main開始的,所以我們也從那裡開始把!首先,...