<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          來聊一聊 Go 的相對路徑問題

          共 4716字,需瀏覽 10分鐘

           ·

          2021-08-23 09:46

          大家好,我是煎魚。

          Go 語言中存在各種運行方式,如何正確的引用文件路徑成為一個值得商議的問題

          以我的一個老 Demo gin-blog 為例,當(dāng)我們在項目根目錄下運行。

          無論是執(zhí)行 go run main.go 時能夠正常運行,執(zhí)行 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)
          ...

          在不同的目錄層級下,不同的方式運行,又是怎么樣的呢,帶著我們的疑問去學(xué)習(xí)!

          問題

          go run

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

          [$ src]# go run gin-blog/main.go
          2018/03/12 16:06:13 Fail to parse 'conf/app.ini': open conf/app.ini: no such file or directory
          exit status 1

          go build

          使用 go build 命令,執(zhí)行 ./gin-blog/main。如下:

          [$ src]# ./gin-blog/main
          2018/03/12 16:49:35 Fail to parse 'conf/app.ini': open conf/app.ini: no such file or directory

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

          我們通過分析得知,Go 運行的相對路徑是相對于執(zhí)行命令時的目錄,自然也就讀取不到了。

          思考

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

          我們想到相對路徑是相對執(zhí)行命令的目錄,那么我們獲取可執(zhí)行文件的地址,拼接起來不就好了嗎?

          實踐

          我們編寫獲取當(dāng)前可執(zhí)行文件路徑的方法:

          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())

          我們分別執(zhí)行以下兩個命令,查看輸出結(jié)果。

          1、 go run

          $ go run main.go
          2018/03/12 18:45:40 /tmp/go-build962610262/b001/exe

          2、 go build

          $ ./main
          2018/03/12 18:49:44 $GOPATH/src/gin-blog

          剖析

          我們聚焦在 go run 的輸出結(jié)果上,發(fā)現(xiàn)它是一個臨時文件的地址,這是為什么呢?

          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 執(zhí)行時會將文件放到 /tmp/go-build... 目錄下,編譯并運行。

          因此go run main.go出現(xiàn)/tmp/go-build962610262/b001/exe結(jié)果也不奇怪了,因為它已經(jīng)跑到臨時目錄下去執(zhí)行可執(zhí)行文件了。

          思考

          這就已經(jīng)很清楚了,那么我們想想,會出現(xiàn)哪些問題呢。如下:

          • 依賴相對路徑的文件,出現(xiàn)路徑出錯的問題。
          • go rungo build 不一樣,一個到臨時目錄下執(zhí)行,一個可手動在編譯后的目錄下執(zhí)行,路徑的處理方式會不同。
          • 不斷go run,不斷產(chǎn)生新的臨時文件。

          這其實就是根本原因了,因為 go rungo build 的編譯文件執(zhí)行路徑并不同,執(zhí)行的層級也有可能不一樣,自然而然就出現(xiàn)各種讀取不到的奇怪問題了。

          解決方案

          一、獲取編譯后的可執(zhí)行文件路徑

          1、 將配置文件的相對路徑與GetAppPath()的結(jié)果相拼接,可解決go build main.go的可執(zhí)行文件跨目錄執(zhí)行的問題(如:./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 來補救。

          2、 通過傳遞參數(shù)指定路徑,可解決go run的問題

          package main

          import (
              "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()進(jìn)行多層判斷

          參見 beego 讀取 app.conf 的代碼。

          該寫法可兼容 go build 和在項目根目錄執(zhí)行 go run ,但是若跨目錄執(zhí)行 go run 就不行。

          三、配置全局系統(tǒng)變量

          我們可以通過os.Getenv來獲取系統(tǒng)全局變量,然后與相對路徑進(jìn)行拼接。

          1、 設(shè)置項目工作區(qū)

          簡單來說,就是設(shè)置項目(應(yīng)用)的工作路徑,然后與配置文件、日志文件等相對路徑進(jìn)行拼接,達(dá)到相對的絕對路徑來保證路徑一致。

          參見 gogs 讀取GOGS_WORK_DIR進(jìn)行拼接的代碼。

          2、 利用系統(tǒng)自帶變量

          簡單來說就是通過系統(tǒng)自帶的全局變量,例如$HOME等,將配置文件存放在$HOME/conf/etc/conf下。

          這樣子就能更加固定的存放配置文件,不需要額外去設(shè)置一個環(huán)境變量。

          拓展

          go test 在一些場景下也會遇到路徑問題,因為go test只能夠在當(dāng)前目錄執(zhí)行,所以在執(zhí)行測試用例的時候,你的執(zhí)行目錄已經(jīng)是測試目錄了。

          需要注意的是,如果采用獲取外部參數(shù)的辦法,用 os.args 時,go test -argsgo run、go build 會有命令行參數(shù)位置的不一致問題。

          總結(jié)

          這三種解決方案,在目前可見的開源項目或介紹中都能找到這些的身影。優(yōu)缺點也是顯而易見的,我認(rèn)為應(yīng)在不同項目選定合適的解決方案即可。

          建議大家不要強依賴讀取配置文件的模塊,應(yīng)當(dāng)將其“堆積木”化,需要什么配置才去注冊什么配置變量,可以解決一部分的問題。

          大家又有什么想法呢,一起討論一波?

          關(guān)注煎魚,吸取他的知識 ??



          你好,我是煎魚。高一折騰過前端,參加過國賽拿了獎,大學(xué)搞過 PHP?,F(xiàn)在整 Go,在公司負(fù)責(zé)微服務(wù)架構(gòu)等相關(guān)工作推進(jìn)和研發(fā)。

          從大學(xué)開始靠自己賺生活費和學(xué)費,到出版 Go 暢銷書《Go 語言編程之旅》,再到獲得 GOP(Go 領(lǐng)域最有觀點專家)榮譽,點擊藍(lán)字查看我的出書之路

          日常分享高質(zhì)量文章,輸出 Go 面試、工作經(jīng)驗、架構(gòu)設(shè)計,加微信拉讀者交流群,記得點贊!

          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  综合网站五月天 | 五月天成人网址 | 一级黄色成人影片 | 免费看的av | 超碰色婷婷|