背景 項目交叉編譯為可執行文件之後,在其他目錄執行文件時提示找不到配置文件 解決方案 直接採用以下代碼獲取到實際執行文件的路徑,然後拼接配置文件即可 代碼分析 os.Args是用來獲取命令行執行參數分片的,當使用 時 分片0會是一串複雜的路徑,原因是直接run 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}
}