1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        聊一聊 Go 里的相對(duì)路徑

        共 4552字,需瀏覽 10分鐘

         ·

        2021-09-19 12:46

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

        以我的一個(gè)老 Demo gin-blog 為例,當(dāng)我們?cè)陧?xiàng)目根目錄下運(yùn)行。

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

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

        # 問題

         go run

        我們上移目錄層級(jí),到 $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

        這時(shí)候你要打一個(gè)大大的問號(hào),就是我的程序讀取到什么地方去了?

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

        # 思考

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

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

        # 實(shí)踐

        我們編寫獲取當(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]
        }

        將其放到啟動(dòng)代碼處查看路徑:

        log.Println(GetAppPath())

        我們分別執(zhí)行以下兩個(gè)命令,查看輸出結(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)它是一個(gè)臨時(shí)文件的地址,這是為什么呢?

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

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

        # 思考

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

        • 依賴相對(duì)路徑的文件,出現(xiàn)路徑出錯(cuò)的問題。

        • go ru 和 go build 不一樣,一個(gè)到臨時(shí)目錄下執(zhí)行,一個(gè)可手動(dòng)在編譯后的目錄下執(zhí)行,路徑的處理方式會(huì)不同。

        • 不斷go run,不斷產(chǎn)生新的臨時(shí)文件。

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

        # 解決方案

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

        1、 將配置文件的相對(duì)路徑與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]
        }

        但是這種方式,對(duì)于go run依舊無(wú)效,這時(shí)候就需要 2 來補(bǔ)救。

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

        運(yùn)行:

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

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

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

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

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

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

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

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

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

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

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

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

        # 拓展

        go test 在一些場(chǎng)景下也會(huì)遇到路徑問題,因?yàn)?code style="font-size: inherit;line-height: inherit;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(226, 36, 70);background: rgb(248, 248, 248);">go test只能夠在當(dāng)前目錄執(zhí)行,所以在執(zhí)行測(cè)試用例的時(shí)候,你的執(zhí)行目錄已經(jīng)是測(cè)試目錄了。

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

        # 總結(jié)

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

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

           


        喜歡明哥文章的同學(xué)
        歡迎長(zhǎng)按下圖訂閱!

        ???

        瀏覽 60
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            大鸡巴免费看 | 性生活网站 | 胡桃给旅行者特殊的奖励视频 | 操逼网789 | 美女高潮爽到喷出精子 | 欧美性爱视频一区 | 狂野欧美性猛交xxxx巴西 | 国产丝袜人妻一区二区三区电影 | 婷婷丁香五月天激情 | 久久一道本 |