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


背景

項目交叉編譯為可執行文件之后,在其他目錄執行文件時提示找不到配置文件

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

解決方案

直接采用以下代碼獲取到實際執行文件的路徑,然后拼接配置文件即可

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

代碼分析

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

$ 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方法的作用,官方文檔中是這樣解釋的

// 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.

意思是它會根據傳入的路徑計算出絕對路徑,如果傳入的為相對路徑,那么它會把當前路徑拼接上

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM