Go语言:如何解决读取不到相对路径配置文件问题
程序员文章站
2024-01-07 20:59:16
背景 项目交叉编译为可执行文件之后,在其他目录执行文件时提示找不到配置文件 解决方案 直接采用以下代码获取到实际执行文件的路径,然后拼接配置文件即可 代码分析 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} }