聊一聊,Golang “相對”路徑問題

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

前言

Golang 中存在各種運行方式,如何正確的引用檔案路徑成為一個值得商議的問題

以 gin-blog 為例,當我們在項目根目錄下,執行 go run main.go 時能夠正常運行(go build也是正常的)

[$ gin-blog]# go run main.go[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env:    export GIN_MODE=release - using code:    gin.SetMode(gin.ReleaseMode)[GIN-debug] GET    /api/v1/tags              --> gin-blog/routers/api/v1.GetTags (3 handlers)...

那麼在不同的目錄層級下,不同的方式運行,又是怎麼樣的呢,帶著我們的疑問去學習

問題

  1. go run

我們上移目錄層級,到 $GOPATH/src 下,執行 go run gin-blog/main.go

[$ src]# go run gin-blog/main.go2018/03/12 16:06:13 Fail to parse 'conf/app.ini': open conf/app.ini: no such file or directoryexit status 1
  1. go build,執行 ./gin-blog/main
[$ src]# ./gin-blog/main2018/03/12 16:49:35 Fail to parse 'conf/app.ini': open conf/app.ini: no such file or directory

這時候你要打一個大大的問號,就是我的程式讀取到什麼地方去了

我們通過分析得知,Golang的相對路徑是相對於執行命令時的目錄;自然也就讀取不到了

思考

既然已經知道問題的所在點,我們就可以尋思做點什麼 : )

我們想到相對路徑是相對執行命令的目錄,那麼我們擷取可執行檔的地址,拼接起來不就好了嗎?

實踐

我們編寫擷取當前可執行檔路徑的方法

import (    "path/filepath"    "os"    "os/exec"    "string")func GetAppPath() string {    file, _ := exec.LookPath(os.Args[0])    path, _ := filepath.Abs(file)    index := strings.LastIndex(path, string(os.PathSeparator))    return path[:index]}

將其放到啟動代碼處查看路徑

log.Println(GetAppPath())

我們分別執行以下兩個命令,查看輸出結果

  1. go run
$ go run main.go2018/03/12 18:45:40 /tmp/go-build962610262/b001/exe
  1. go build
$ ./main2018/03/12 18:49:44 $GOPATH/src/gin-blog

剖析

我們聚焦在 go run 的輸出結果上,發現它是一個臨時檔案的地址,這是為什麼呢?

go help run中,我們可以看到

Run compiles and runs the main package comprising the named Go source files.A Go source file is defined to be a file ending in a literal ".go" suffix.

也就是 go run 執行時會將檔案放到 /tmp/go-build... 目錄下,編譯並運行

因此go run main.go出現/tmp/go-build962610262/b001/exe結果也不奇怪了,因為它已經跑到臨時目錄下去執行可執行檔了

這就已經很清楚了,那麼我們想想,會出現哪些問題呢

  • 依賴相對路徑的檔案,出現路徑出錯的問題
  • go rungo build 不一樣,一個到臨時目錄下執行,一個可手動在編譯後的目錄下執行,路徑的處理方式會不同
  • 不斷go run,不斷產生新的臨時檔案

這其實就是根本原因了,因為 go rungo build 的編譯檔案執行路徑並不同,執行的層級也有可能不一樣,自然而然就出現各種讀取不到的奇怪問題了

解決方案

一、擷取編譯後的可執行檔路徑

  1. 將設定檔的相對路徑與GetAppPath()的結果相拼接,可解決go build main.go的可執行檔跨目錄執行的問題(如:./src/gin-blog/main
import (    "path/filepath"    "os"    "os/exec"    "string")func GetAppPath() string {    file, _ := exec.LookPath(os.Args[0])    path, _ := filepath.Abs(file)    index := strings.LastIndex(path, string(os.PathSeparator))    return path[:index]}

但是這種方式,對於go run依舊無效,這時候就需要2來補救

  1. 通過傳遞參數指定路徑,可解決go run的問題
package mainimport (    "flag"    "fmt")func main() {    var appPath string    flag.StringVar(&appPath, "app-path", "app-path")    flag.Parse()    fmt.Printf("App path: %s", appPath)}

運行

go run main.go --app-path "Your project address"

二、增加os.Getwd()進行多層判斷

參見 beego 讀取 app.conf 的代碼

該寫法可相容 go build 和在項目根目錄執行 go run ,但是若跨目錄執行 go run 就不行

三、配置全域系統變數

我們可以通過os.Getenv來擷取系統全域變數,然後與相對路徑進行拼接

  1. 設定項目工作區

簡單來說,就是設定項目(應用)的工作路徑,然後與設定檔、記錄檔等相對路徑進行拼接,達到相對的絕對路徑來保證路徑一致

參見 gogs 讀取GOGS_WORK_DIR進行拼接的代碼

  1. 利用系統內建變數

簡單來說就是通過系統內建的全域變數,例如$HOME等,將設定檔存放在$HOME/conf/etc/conf

這樣子就能更加固定的存放設定檔,不需要額外去設定一個環境變數

(這點今早與一位SFer討論了一波,感謝)

拓展

go test 在一些情境下也會遇到路徑問題,因為go test只能夠在目前的目錄執行,所以在執行測試案例的時候,你的執行目錄已經是測試目錄了

就會產生類似的問題

小結

這三種解決方案,在目前可見的開源項目或介紹中都能找到這些的身影

優缺點也是顯而易見的,我認為應在不同項目選定合適的解決方案即可

大家又有什麼想法呢,在SF 這裡 討論一波?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.