【问题标题】:How to get the directory of the package the file is in, not the current working directory如何获取文件所在包的目录,而不是当前工作目录
【发布时间】:2015-08-23 05:17:20
【问题描述】:

我正在制作一个包来对服务进行 API 调用。

我有一个测试包,我只是用它来测试 API 调用并测试我刚刚 include 另一个包进入的主包的功能。

在我正在处理的主包中,我有

ioutil.ReadFile(filepath.Abs("Filename.pub"))

这没关系,但是当我从我的测试包中调用它时,例如

/Users/####/gocode/src/github.com/testfolder go run main.go

它告诉我

panic: open /Users/####/gocode/src/github.com/testfolder/public.pub: no such file or directory

问题是,它是在testfolder 内部寻找public.pub,而不是它所在的github.com/apipackage/

只是为了澄清这些乱七八糟的词:

API包有一个从同一目录读取的函数

但是因为我包含了 API 包,而当我 go run main.go 时,Testfolder 是 CWD,它反而试图从 testfolder 获取它,即使 main.go 没有该功能并且只是包括它。

【问题讨论】:

    标签: go


    【解决方案1】:

    runtime.Caller 是你想要的,我相信。

    这是一个演示:

    package main
    
    import (
        "fmt"
        "runtime"
        "path"
    )
    
    func main() {
        _, filename, _, ok := runtime.Caller(0)
        if !ok {
            panic("No caller information")
        }
        fmt.Printf("Filename : %q, Dir : %q\n", filename, path.Dir(filename))
    }
    

    https://play.golang.org/p/vVa2q-Er6D

    【讨论】:

    • 当我对此进行快速测试时,当前文件在我调试测试时位于runtime.Caller(3),因此不能保证(0) 是您想要的值。
    • @jcollum 如果您将runtime.Caller 调用包装在不在您想要的目录中的函数中,您只需要增加此数字。通常,如果你用这个 sn-p 创建一个辅助函数,你会用 1 替换 0
    • 我认为没有办法真正确定运行时需要哪个级别的 runtime.Caller。如果可以,最好不要依赖它。
    • 你可以,即使是静态的。这是文档所说的:The argument skip is the number of stack frames to ascend, with 0 identifying the caller of Caller.
    • 它主要用于测试。编译后,“源文件的路径”的概念确实没用。
    【解决方案2】:

    从 Go 1.16 开始,您可以使用 embed 包。这允许您将文件嵌入到正在运行的 go 程序中。引用的文件需要位于或低于嵌入文件。在您的情况下,结构如下所示:

    -- apipackage
      \- public.pub
      \- apipackage.go
    -- testfolder
      \- main.go
    

    您可以使用 go 指令引用该文件

    // apipackage.go
    package apipackage
    
    import (
      "embed"
    )
    
    //go:embed public.pub
    var content embed.FS
    
    func GetText() string {
      text, _ := content.ReadFile("public.pub")
      return text
    }
    

    无论程序在哪里执行,这个程序都会成功运行。

    【讨论】:

      最近更新 更多