• 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}
    }
    
  • 相关阅读:
    23种设计模式-桥接模式
    23种设计模式-单列模式
    23种设计模式-迭代器模式
    23种设计模式-组合模式
    23种设计模式-备忘录模式
    23种设计模式-适配器模式
    23种设计模式-状态模式
    SVN的安装和应用
    线程、线程池
    条形码
  • 原文地址:https://www.cnblogs.com/pingyeaa/p/12495285.html
Copyright © 2020-2023  润新知