Go語言:如何解決讀取不到相對路徑配置文件問題

背景

項目交叉編譯爲可執行文件以後,在其餘目錄執行文件時提示找不到配置文件shell

2020/03/14 20:44:23 配置文件讀取失敗 open config.ini: no such file or directory

解決方案

直接採用如下代碼獲取到實際執行文件的路徑,而後拼接配置文件便可bash

file, _ := exec.LookPath(os.Args[0])
path, _ := filepath.Abs(file)
index := strings.LastIndex(path, string(os.PathSeparator))
path = path[:index]

代碼分析

os.Args是用來獲取命令行執行參數分片的,當使用go runui

$ go run main.go
[/var/folders/3s/5v6r481x17x5ks_7q1dzmlsw0000gp/T/go-build231739964/b001/exe/main]

分片0會是一串複雜的路徑,緣由是直接run go文件時會將文件轉移到臨時路徑下,而後再進行編譯和執行,若是直接執行編譯後的文件就不同了,此時分片0爲執行文件的相對路徑命令行

$ go build
$ ./jira_reminder
[./jira-reminder]

接下來看一下LookPath方法的做用,官方文檔中是這樣解釋的code

// LookPath searches for an executable named file in the
// directories named by the PATH environment variable.
// If file contains a slash, it is tried directly and the PATH is not consulted.
// The result may be an absolute path or a path relative to the current directory.

大體意思就是它會去環境變量中找這個可執行文件的絕對路徑,或相對於當前目錄的路徑。接下來執行了filepath.Abs方法遞歸

// Abs returns an absolute representation of path.
// If the path is not absolute it will be joined with the current
// working directory to turn it into an absolute path. The absolute
// path name for a given file is not guaranteed to be unique.
// Abs calls Clean on the result.

意思是它會根據傳入的路徑計算出絕對路徑,若是傳入的爲相對路徑,那麼它會把當前路徑拼接上element

此時返回的path是一個包含可執行文件在內的完整路徑,咱們只須要精確到目錄便可rem

index := strings.LastIndex(path, string(os.PathSeparator))

以上代碼會搜索最後一個目錄分隔符的位置(下標),而後經過如下代碼將路徑中下標後面的字符串切割掉文檔

path = path[:index]

這樣就完成了目錄的獲取,接下來再拼接上咱們實際的配置文件就能夠了字符串

番外

發現不調用exec.LookPath也是能夠達到查詢絕對路徑的目的的,那麼exec.LookPath還有什麼用?

path, _ := filepath.Abs(os.Args[0])
index := strings.LastIndex(path, string(os.PathSeparator))
path = path[:index]

咱們來看一下源碼,exec.LookPath的做用是從相對路徑或環境變量PATH中遞歸找可執行文件,這起着一個校驗的做用,檢測調用的可執行文件是否是真的存在,若是存在再繼續往下拼接出絕對路徑,由於咱們的執行文件的確是存在的,因此就算不使用exec.LookPath也能夠達到目的

func LookPath(file string) (string, error) {
    // NOTE(rsc): I wish we could use the Plan 9 behavior here
    // (only bypass the path if file begins with / or ./ or ../)
    // but that would not match all the Unix shells.

    if strings.Contains(file, "/") {
        err := findExecutable(file)
        if err == nil {
            return file, nil
        }
        return "", &Error{file, err}
    }
    path := os.Getenv("PATH")
    for _, dir := range filepath.SplitList(path) {
        if dir == "" {
            // Unix shell semantics: path element "" means "."
            dir = "."
        }
        path := filepath.Join(dir, file)
        if err := findExecutable(path); err == nil {
            return path, nil
        }
    }
    return "", &Error{file, ErrNotFound}
}
相關文章
相關標籤/搜索