亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

引言

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

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

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

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

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

如果想找到這個文件,可通過 go run -work 保留文件,通過 os.Args[0] 確認文件路徑。

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í)慣于動態(tài)語言中獲取路徑的做法,在 Go 中通過相對于可執(zhí)行文件的路徑來定位其他文件,使用 go run 調(diào)試的時候,就可能會引起一定的困惑。

下面開始進入正題,詳細 Go 中的文件路徑的不同獲取方式吧。

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

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

我將介紹實現(xiàn)這個目標的兩種方式。

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

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

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

于是,為了保險起見,我們可通過 exec.LookPath 對 os.Args[0] 做一個處理。

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

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

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

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

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

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

使用 os.Executable

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

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

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

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

但,這兩種方式都沒有解決一個問題:如果執(zhí)行文件是符號鏈接,不會返回真正的可執(zhí)行文件。

符號鏈接

我們可通過使用 filepath.EvalSymlinks 來獲取符號鏈接實際指向的路徑。

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

兼容 go run 與 go build

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

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

大概意思是,通過判斷執(zhí)行文件的運行目錄或手動添加環(huán)境變量標識當(dāng)前位于 go run 運行模式。如果處理 go run 模式下,我們再通過相對于源碼文件位置定位其他文件。

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

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

或者是

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

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

而在入口函數(shù) main 中,通過 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)
}

除了那個獲取源碼文件位置的函數(shù) runtime.Caller,這個代碼并不復(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ù))的程序計數(shù)器(PC)、文件名、代碼行號、一個布爾值,布爾值表示獲取信息是否成功。我們關(guān)心的是源碼文件路徑,runtime.Caller 返回的文件名可以用來確定當(dāng)前執(zhí)行代碼的位置。

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

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

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

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

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

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

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

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

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

log_path = "/var/log/"

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

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

export ROOTDIR=`pwd`

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

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

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

如果是運行在 Docker 中,可通過 WORKDIR 指定工作目錄,問題也變得簡單很多,程序相對當(dāng)前的當(dāng)前目錄就是這個特定的工作目錄。

總結(jié)

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

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

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

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

相關(guān)文章

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

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

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

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

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

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

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

    go modules中replace使用方法

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

    Go語言對JSON進行編碼和解碼的方法

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

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

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

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

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

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

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

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

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

    利用Go語言實現(xiàn)二叉搜索樹

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

最新評論