Golang中如何通过runtime.Caller获取相对路径

Golang中如何通过runtime.Caller获取相对路径 我有一个函数,它使用调用者文件路径(来自 runtime.Caller(1) 的 fn)返回新的错误。

但我想要获取相对路径(相对于执行 go build 的目录,也就是 main.go 所在的目录)。

如何优雅地实现这一点?

func NewError(e interface{}) error {
	if e != nil {
		_, fn, line, _ := runtime.Caller(1)
		return fmt.Errorf("[error] %s:%d %v", fn, line, e)
	}
	return nil
}

更多关于Golang中如何通过runtime.Caller获取相对路径的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

使用 os.Args

它包含启动 Go 程序的所有参数,包括 Go 二进制文件路径(os.Args[0])。

更多关于Golang中如何通过runtime.Caller获取相对路径的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Christophe_Meessen:

initRelative

这将是 main.go 的目录,通过两次使用 filepath.Dir 实现…

func main() {
    fmt.Println("hello world")
}

如果将二进制文件移动到其他位置,Go二进制路径可能会改变

但是runtime.Caller返回的函数不会改变

在文件 relative.go 中定义以下内容:

package main

import (
    "runtime"
    "filepath"
)

func init() {
    initRelative()
}

var prefixPath string

func initRelative() {
    _, fileName, _, _ := runtime.Caller(0)
    prefixPath = filepath.Dir(fileName)
} 

func relative(path string) return {
    if filepath.HasPrefix(path, prefixPath) {
        return path[len(prefixPath):]
    }
    return path
}

这段代码假设文件 relative.go 存储在构建根目录中。您可能需要修改代码以匹配您的具体使用场景。

这比我的代码更好。但是relative.go在utils包中,而filepath.Dir(filepath.Dir(fileName))只移除了fileName末尾的relative.go。所以prefixPath最终会以"…/utils/“结尾。因此,不以”…/utils/"开头的路径将不会被转换为相对路径。

为了解决这个问题,我建议你将initRelative函数修改如下。

func initRelative() {
	_, fileName, _, _ := runtime.Caller(0)
	prefixPath = fileName[:len(fileName)-len("utils/relative.go")]
}

这应该将prefixPath设置为你项目的根路径。

感谢!

utils/relative.go

package utils

import (
	"path/filepath"
	"runtime"
	"strings"
)

func init() {
	initRelative()
}

var prefixPath string

func initRelative() {
	_, fileName, _, _ := runtime.Caller(0)
	prefixPath = filepath.ToSlash(filepath.Dir(filepath.Dir(fileName))) + "/"
}

func relative(path string) string {
	return strings.TrimPrefix(filepath.ToSlash(path), prefixPath)
}

utils/functions.go

package utils

import (
	"fmt"
	"os"
	"runtime"
)

// NewError :
func NewError(e interface{}) error {
	if e != nil {
		_, fn, line, _ := runtime.Caller(1)
		return fmt.Errorf("[error] %s:%d %v", relative(fn), line, e)
	}
	return nil
}

更新:

使用 filepath.ToSlash(跨平台)

在Go中,要获取相对于构建目录的路径,可以通过比较runtime.Caller返回的绝对路径与构建时的当前工作目录来实现。以下是实现方法:

import (
    "fmt"
    "path/filepath"
    "runtime"
    "os"
)

// 获取构建时的当前工作目录(通常是main.go所在目录)
var buildDir string

func init() {
    // 获取可执行文件所在目录
    exePath, err := os.Executable()
    if err != nil {
        buildDir, _ = os.Getwd()
        return
    }
    buildDir = filepath.Dir(exePath)
}

func NewError(e interface{}) error {
    if e != nil {
        _, fn, line, _ := runtime.Caller(1)
        
        // 获取相对于构建目录的路径
        relPath, err := filepath.Rel(buildDir, fn)
        if err != nil {
            // 如果无法获取相对路径,回退到文件名
            relPath = filepath.Base(fn)
        }
        
        return fmt.Errorf("[error] %s:%d %v", relPath, line, e)
    }
    return nil
}

这个实现的关键点:

  1. init函数中获取构建目录路径
  2. 使用filepath.Rel()计算相对路径
  3. 提供错误处理,在无法计算相对路径时回退到文件名

示例使用:

func someFunction() error {
    // 假设项目结构:
    // /project/main.go
    // /project/pkg/utils/errors.go
    
    return NewError("something went wrong")
    // 输出: [error] pkg/utils/errors.go:15 something went wrong
}

如果runtime.Caller返回的路径是/home/user/project/pkg/utils/errors.go,而构建目录是/home/user/project,那么相对路径就是pkg/utils/errors.go

这种方法在大多数情况下都能正常工作,包括在开发环境和生产部署中。

回到顶部