欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Go語(yǔ)言中獲取文件路徑的不同方法與應(yīng)用場(chǎng)景

 更新時(shí)間:2024年02月22日 10:40:04   作者:波羅學(xué)  
在使用?Go?開(kāi)發(fā)項(xiàng)目時(shí),估計(jì)有不少人遇到過(guò)無(wú)法正確處理文件路徑的問(wèn)題,本文將嘗試從簡(jiǎn)單到復(fù)雜,詳細(xì)介紹?Go?中獲取路徑的不同方法及應(yīng)用場(chǎng)景,希望對(duì)大家有所幫助

在使用 Go 開(kāi)發(fā)項(xiàng)目時(shí),估計(jì)有不少人遇到過(guò)無(wú)法正確處理文件路徑的問(wèn)題,特別是剛從如 PHP、python 這類(lèi)動(dòng)態(tài)語(yǔ)言轉(zhuǎn)向 Go 的朋友,已經(jīng)習(xí)慣了通過(guò)相對(duì)源碼文件找到其他文件。這個(gè)問(wèn)題能否合理解決,不僅關(guān)系到程序的可移植性,還直接影響到程序的穩(wěn)定性和安全性。

本文將嘗試從簡(jiǎn)單到復(fù)雜,詳細(xì)介紹 Go 中獲取路徑的不同方法及應(yīng)用場(chǎng)景。

引言

首先,為什么要獲取文件路徑?

一般來(lái)說(shuō),程序在運(yùn)行時(shí)必須準(zhǔn)確地讀取相關(guān)的配置和資源以順利啟動(dòng)。確定這些信息的存儲(chǔ)位置,即獲取文件路徑,成為了正確訪問(wèn)這些信息的首要步驟,對(duì)于構(gòu)建穩(wěn)定可靠的應(yīng)用程序而言至關(guān)重要。

其次,為什么從動(dòng)態(tài)語(yǔ)言轉(zhuǎn)到 Go,容易被這個(gè)問(wèn)題困擾?

與 Go(一種靜態(tài)語(yǔ)言)相比,動(dòng)態(tài)語(yǔ)言通過(guò)直接解釋腳本文件而執(zhí)行的。這一機(jī)制使得動(dòng)態(tài)語(yǔ)言在路徑獲取方面更為直觀和易懂。然而,Go語(yǔ)言將源代碼編譯成獨(dú)立的二進(jìn)制可執(zhí)行文件,這導(dǎo)致可執(zhí)行文件與源代碼間缺乏直接的聯(lián)系。

為了簡(jiǎn)化調(diào)試過(guò)程,Go 通過(guò) go run 命令提供了一種類(lèi)似動(dòng)態(tài)語(yǔ)言直接執(zhí)行源代碼的便捷方式,實(shí)質(zhì)上是將構(gòu)建和運(yùn)行步驟合二為一。這個(gè)過(guò)程中,會(huì)生成一個(gè)臨時(shí)可執(zhí)行文件,但這個(gè)文件不是存在當(dāng)前工作目錄中,這又為理解上帶來(lái)額外的挑戰(zhàn)。

如果想找到這個(gè)文件,可通過(guò) go run -work 保留文件,通過(guò) os.Args[0] 確認(rèn)文件路徑。

func main() {
    fmt.Println(os.Args[0])
}

輸出:

$ go run -work main.go
WORK=/var/folders/0b/v4r1lzyj0n566qgd8dt_km4c0000gn/T/go-build1458488796
/var/folders/0b/v4r1lzyj0n566qgd8dt_km4c0000gn/T/go-build1458488796/b001/exe/main

可執(zhí)行文件就是位于 $WORK/b001/exe/ 的 main 文件。

若你習(xí)慣于動(dòng)態(tài)語(yǔ)言中獲取路徑的做法,在 Go 中通過(guò)相對(duì)于可執(zhí)行文件的路徑來(lái)定位其他文件,使用 go run 調(diào)試的時(shí)候,就可能會(huì)引起一定的困惑。

下面開(kāi)始進(jìn)入正題,詳細(xì) Go 中的文件路徑的不同獲取方式吧。

相對(duì)于執(zhí)行文件獲取路徑

之前提到了那么多在 Go 中獲取可執(zhí)行文件路徑時(shí)可能導(dǎo)致的問(wèn)題,我們就先從如何獲取當(dāng)前執(zhí)行文件的路徑開(kāi)始吧。

我將介紹實(shí)現(xiàn)這個(gè)目標(biāo)的兩種方式。

命令行參數(shù) os.Args[0]

第一種方式是通過(guò)命令行參數(shù) os.Args[0]os.Args 是一個(gè)字符串切片,包含啟動(dòng)程序時(shí)傳遞給它的命令行參數(shù)。os.Args[0] 是這個(gè)切片的第一個(gè)元素,通常表示程序的執(zhí)行文件路徑。引言部分的演示示例,我就是通過(guò)這種方式獲取執(zhí)行文件的路徑的。

這個(gè)方式缺點(diǎn)是,依賴(lài)于可執(zhí)行文件是被調(diào)用的方式,它可能是一個(gè)相對(duì)路徑、一個(gè)絕對(duì)路徑,或者僅僅是程序名。

于是,為了保險(xiǎn)起見(jiàn),我們可通過(guò) exec.LookPath 對(duì) os.Args[0] 做一個(gè)處理。

fmt.Println(exec.LookPath(os.Args[0]))

這個(gè)函數(shù)的作用是,輸入?yún)?shù) filename 中如果包含如 / 字符,直接返回 filename,否則會(huì)從 PATH 環(huán)境變量中尋找名為 filename 的可執(zhí)行文件。這就解決了僅僅通過(guò)程序名調(diào)用無(wú)法獲取文件路徑的問(wèn)題。

我是在 MacOS 上測(cè)試的,這段邏輯是在 lp_unix.go 文件中,window 應(yīng)該是不同的邏輯,windows 的文件路徑分隔符和類(lèi) unix 不同,或者也有其他復(fù)雜邏輯。

另外,它獲取到的可能是相對(duì)路徑也可能是絕對(duì)路徑。如果希望得到絕對(duì)路徑,要通過(guò) filepath.Abs 處理下。

exePath, _ := exec.LookPath(os.Args[0])
fmt.Println(filepath.Abs(exePath))

但這種不是最優(yōu)的方式,明顯是繞的遠(yuǎn)了。我提這個(gè)方法是為了順便介紹下 exec.LookPath 和 filepath.Abs 這兩個(gè)函數(shù)。

使用 os.Executable

獲取當(dāng)前 Go 程序的執(zhí)行文件路徑最優(yōu)的解法是,使用 os.Executable 函數(shù)。這個(gè)方法會(huì)返回可執(zhí)行文件的絕對(duì)路徑。

fmt.Println(os.Executable()) // 

輸出:

$ go run -work main.go
WORK=/var/folders/0b/v4r1lzyj0n566qgd8dt_km4c0000gn/T/go-build1458488796
/var/folders/0b/v4r1lzyj0n566qgd8dt_km4c0000gn/T/go-build1458488796/b001/exe/main

這個(gè)值在 go 啟動(dòng)時(shí),運(yùn)行時(shí)自動(dòng)解析到內(nèi)存的值,而調(diào)用 os.Executable 實(shí)際就是直接從這個(gè)變量中獲取,沒(méi)有額外的處理。

它的性能相對(duì)于前面的通過(guò)幾個(gè)函數(shù)組合實(shí)現(xiàn)的方式,肯定是吊打前者。

但,這兩種方式都沒(méi)有解決一個(gè)問(wèn)題:如果執(zhí)行文件是符號(hào)鏈接,不會(huì)返回真正的可執(zhí)行文件。

符號(hào)鏈接

我們可通過(guò)使用 filepath.EvalSymlinks 來(lái)獲取符號(hào)鏈接實(shí)際指向的路徑。

realPath,  _:= filepath.EvalSymlinks(exePath)
fmt.Println("Real path of executable:", realPath)

兼容 go run 與 go build

講了那么多關(guān)于獲取當(dāng)前執(zhí)行文件路徑的方案,但如何解決由 go run 臨時(shí)文件產(chǎn)生的問(wèn)題呢?

我的建議是,換個(gè)思路,不要把拘泥在相對(duì)于可執(zhí)行文件定位其他文件路徑這一個(gè)方向上。我在網(wǎng)上看到過(guò)通過(guò)判斷是否是 go run 運(yùn)行實(shí)現(xiàn)的適配方案。

大概意思是,通過(guò)判斷執(zhí)行文件的運(yùn)行目錄或手動(dòng)添加環(huán)境變量標(biāo)識(shí)當(dāng)前位于 go run 運(yùn)行模式。如果處理 go run 模式下,我們?cè)偻ㄟ^(guò)相對(duì)于源碼文件位置定位其他文件。

嘗試實(shí)現(xiàn)下吧。

// isGoRun 檢查當(dāng)前是否處于 go run 模式
func isGoRun() bool {
    // 檢查環(huán)境變量(如果你選擇設(shè)置一個(gè)特定的環(huán)境變量來(lái)標(biāo)識(shí))
    if _, ok := os.LookupEnv("GO_RUN_MODE"); ok {
        return true
    }
}

或者是

func isGoRun() bool {
    // 或者通過(guò)分析 executable 路徑的特征來(lái)判斷
    exePath, err := os.Executable()
    if err != nil {
        fmt.Println("Error getting executable path:", err)
        return false
    }

    // 示例中僅僅檢查路徑是否包含臨時(shí)目錄特征,實(shí)際情況可能需要更復(fù)雜的邏輯
    return exePath[:5] == "/var/" {
}

而在入口函數(shù) main 中,通過(guò) runtime.Caller(0) 獲取源碼文件路徑。

func EntryPath() string {
    if IsGoRun() {
        _, file, _, ok := runtime.Caller(0)
        if ok {
            return filepath.Dir(file)
        }
    } else {
        path, _ := os.Executable()
        return filepath.Dir(path)
    }
    return "./"
}

func main() {
    configPath := filepath.Join(EntryPath(), "config.json")
    fmt.Println("ConfigPath:", configPath)
}

除了那個(gè)獲取源碼文件位置的函數(shù) runtime.Caller,這個(gè)代碼并不復(fù)雜。runtime.Caller 函數(shù)用于獲取當(dāng)前函數(shù)的調(diào)用棧信息。

它的函數(shù)簽名,如下所示:

func Caller(skip int) (pc uintptr, file string, line int, ok bool)

返回信息有調(diào)用者(main 函數(shù))的程序計(jì)數(shù)器(PC)、文件名、代碼行號(hào)、一個(gè)布爾值,布爾值表示獲取信息是否成功。我們關(guān)心的是源碼文件路徑,runtime.Caller 返回的文件名可以用來(lái)確定當(dāng)前執(zhí)行代碼的位置。

看到這里,不知道是不是有人發(fā)出疑問(wèn),竟然通過(guò)能定位源碼文件位置,為什么還要另外一種方式。這是源碼文件的位置不會(huì)因執(zhí)行文件的移動(dòng)而變動(dòng)。舉例來(lái)說(shuō),如果 main.go 文件在 /Users/poloxue/ 下構(gòu)建出 main 執(zhí)行文件。我將其移動(dòng)到其他目錄,甚至是服務(wù)器上,它的路徑依然是 /Users/poloxue/main.go。

現(xiàn)在,即使在 go run 模式下,依然能正確定位其他文件的路徑了。

這種方式看起來(lái)挺不錯(cuò)的,但我不推薦。我的建議是,為項(xiàng)目定義清晰明確的規(guī)則來(lái)管理配置和資源文件的路徑。

定義配置和資源的路徑規(guī)則

常見(jiàn)的是用絕對(duì)路徑規(guī)則指定配置和資源文件路徑,如 Linux 或其他類(lèi) Unix 系統(tǒng)有一套 XDG 基準(zhǔn)規(guī)則(XDG Base Directory Specification),有興趣可了解下。

或者是另一套更常見(jiàn)被用于日常項(xiàng)目中的方案,通過(guò)環(huán)境變量或其他方式設(shè)置固定的項(xiàng)目根目錄或工作目錄,而其他文件路徑皆相對(duì)于這個(gè)固定不變目錄的位置。

$RootDir/config.yaml
$RootDir/logs/
$RootDir/resources/
$RootDir/static

實(shí)際上,這種方式更常見(jiàn)于平時(shí)的項(xiàng)目中。無(wú)論可執(zhí)行文件被放在什么路徑下,都不會(huì)對(duì)其他文件的路徑位置產(chǎn)生影響。

如果希望文件路徑支持自定義,可在配置中提供路徑配置項(xiàng),或通過(guò)命令行選項(xiàng)的方式傳遞。

log_path = "/var/log/"

$ go run main.go --config-path "./config.toml"

如果覺(jué)得每次 go run 都要帶上環(huán)境變量麻煩,可提前設(shè)置環(huán)境變量

export ROOTDIR=`pwd`

我們也可以在 IDE 中設(shè)置項(xiàng)目級(jí)別的環(huán)境變量。

亦或是提供默認(rèn)值,如果 ROOTDIR 為空,默認(rèn)項(xiàng)目根目錄為 ./,即當(dāng)前路徑,

# ROOTDIR=./ go run main.go
$ go run main.go

如果是運(yùn)行在 Docker 中,可通過(guò) WORKDIR 指定工作目錄,問(wèn)題也變得簡(jiǎn)單很多,程序相對(duì)當(dāng)前的當(dāng)前目錄就是這個(gè)特定的工作目錄。

總結(jié)

在 Go 項(xiàng)目中正確處理文件路徑是確保程序可移植性、穩(wěn)定性和安全性的關(guān)鍵。與動(dòng)態(tài)語(yǔ)言不同,Go編譯成二進(jìn)制可執(zhí)行文件,使得直接關(guān)聯(lián)源碼和運(yùn)行時(shí)文件變得復(fù)雜。

本文介紹了多種獲取文件路徑的方法,包括 os.Args[0]、exec.LookPath、filepath.Abs和 os.Executable,并討論了如何通過(guò)判斷是否是 go run 運(yùn)行來(lái)兼容 go run 和go build 的路徑問(wèn)題。

最后,建議定義清晰的規(guī)則管理配置和資源文件路徑,使用環(huán)境變量或配置項(xiàng)指定路徑,避免依賴(lài)于可執(zhí)行文件位置,以求提高 Go 項(xiàng)目的健壯性。

到此這篇關(guān)于詳解Go語(yǔ)言中獲取文件路徑的不同方法與應(yīng)用場(chǎng)景的文章就介紹到這了,更多相關(guān)Go獲取文件路徑內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang channel死鎖的幾種情況小結(jié)

    Golang channel死鎖的幾種情況小結(jié)

    本文主要介紹了Golang channel死鎖的幾種情況小結(jié),詳細(xì)的介紹了六種情況,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • VSCode配置Go插件和第三方拓展包的詳細(xì)教程

    VSCode配置Go插件和第三方拓展包的詳細(xì)教程

    這篇文章主要介紹了VSCode配置Go插件和第三方拓展包的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • 使用Go實(shí)現(xiàn)優(yōu)雅重啟服務(wù)功能

    使用Go實(shí)現(xiàn)優(yōu)雅重啟服務(wù)功能

    這篇文章主要介紹了如何使用Go來(lái)實(shí)現(xiàn)優(yōu)雅重啟服務(wù),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-11-11
  • go modules中replace使用方法

    go modules中replace使用方法

    這篇文章主要為大家介紹了go modules中replace使用方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語(yǔ)言對(duì)JSON進(jìn)行編碼和解碼的方法

    Go語(yǔ)言對(duì)JSON進(jìn)行編碼和解碼的方法

    這篇文章主要介紹了Go語(yǔ)言對(duì)JSON進(jìn)行編碼和解碼的方法,涉及Go語(yǔ)言操作json的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • golang?gorm錯(cuò)誤處理事務(wù)以及日志用法示例

    golang?gorm錯(cuò)誤處理事務(wù)以及日志用法示例

    這篇文章主要為大家介紹了golang?gorm錯(cuò)誤處理事務(wù)以及日志用法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬(wàn)次的下單請(qǐng)求

    go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬(wàn)次的下單請(qǐng)求

    這篇文章主要為大家介紹了go?zero微服務(wù)實(shí)戰(zhàn)處理每秒上萬(wàn)次的下單請(qǐng)求示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Go?gRPC服務(wù)雙向流式RPC教程

    Go?gRPC服務(wù)雙向流式RPC教程

    這篇文章主要為大家介紹了Go?gRPC服務(wù)雙向流式RPC教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 使用gRPC實(shí)現(xiàn)獲取數(shù)據(jù)庫(kù)版本

    使用gRPC實(shí)現(xiàn)獲取數(shù)據(jù)庫(kù)版本

    這篇文章主要為大家詳細(xì)介紹了如何使用gRPC實(shí)現(xiàn)獲取數(shù)據(jù)庫(kù)版本,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • 利用Go語(yǔ)言實(shí)現(xiàn)二叉搜索樹(shù)

    利用Go語(yǔ)言實(shí)現(xiàn)二叉搜索樹(shù)

    二叉樹(shù)是一種常見(jiàn)并且非常重要的數(shù)據(jù)結(jié)構(gòu),在很多項(xiàng)目中都能看到二叉樹(shù)的身影,當(dāng)然它也有很多變種,本文要介紹的是二叉搜索樹(shù)的實(shí)現(xiàn),希望對(duì)大家有所幫助
    2023-07-07

最新評(píng)論