【问题标题】:How to get the directory of the currently running file?如何获取当前运行文件的目录?
【发布时间】:2013-09-03 10:33:39
【问题描述】:

在 nodejs 中,我使用 __dirname 。这在 Golang 中相当于什么?

我在谷歌上搜索并找到了这篇文章 http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ 。他在哪里使用下面的代码

_, filename, _, _ := runtime.Caller(1)
f, err := os.Open(path.Join(path.Dir(filename), "data.csv"))

但这在 Golang 中是正确的方式还是惯用的方式?

【问题讨论】:

  • 这不是您问题的答案,但您可以缓存全局变量的路径(运行时不能更改您的文件位置:))不要每次运行 os.open你的代码运行
  • 你应该将0,而不是1,传递给runtime.Caller()
  • runtime.Caller(0) 会给你源文件的路径,比如$GOPATH/src/packagename/main.go。该线程中的其他答案试图返回可执行文件的路径(如$GOPATH/bin/packagename)。
  • 你假设程序是从一个文件中运行的......

标签: go


【解决方案1】:

编辑:从 Go 1.8(2017 年 2 月发布)开始,推荐使用 os.Executable

func Executable() (string, error)

Executable 返回启动当前进程的可执行文件的路径名。不能保证路径仍然指向正确的可执行文件。如果使用符号链接来启动进程,则取决于操作系统,结果可能是符号链接或它指向的路径。如果需要稳定的结果,path/filepath.EvalSymlinks 可能会有所帮助。

要获取可执行文件的目录,您可以使用path/filepath.Dir

Example:

package main

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

func main() {
    ex, err := os.Executable()
    if err != nil {
        panic(err)
    }
    exPath := filepath.Dir(ex)
    fmt.Println(exPath)
}

旧答案:

你应该可以使用os.Getwd

func Getwd() (pwd string, err error)

Getwd 返回对应于当前目录的根路径名。如果当前目录可以通过多个路径到达(由于符号链接),Getwd 可能会返回其中任何一个。

例如:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}

【讨论】:

  • 这是当前进程的工作目录。在 nodejs 中相当于 process.cwd() nodejs.org/api/process.html#process_process_cwd
  • 好的,我看到了区别。你们中的二进制文件在文件系统中的位置(而不是当前工作目录)我认为runtime.Caller 是最接近“惯用”的地方
  • '2017 年 2 月发布'?似乎时间机器已经被发明了,我们有来自未来的成员发布。很高兴知道未来版本将具有可靠的跨平台方法,同时我们必须坚持当前可用的解决方案。
  • @ljgww 抱歉,我会带上我的 Delorean 回家 :-) 我提前更新了我的答案,因为我刚刚看到即将推出的功能并想我会忘记更新答案稍后。
  • 使用path/filepath.Dir 编辑,因为path.Dir 仅适用于正斜杠(Unix 风格)作为目录分隔符。
【解决方案2】:

应该这样做:

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

func main() {
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
    if err != nil {
            log.Fatal(err)
    }
    fmt.Println(dir)
}

【讨论】:

  • 这里有可能出错吗?如果是这样,只是出于好奇,错误是什么?
  • 对我不起作用play.golang.org/p/c8fe-Zm_bH - os.Args[0] 不一定包含 abs 路径。
  • 即使 os.Args[0] 不包含 abs 路径,它实际上也可以工作。操场结果不是您预期的原因是因为它位于沙箱内。
  • 这不是一种可靠的方法,请参阅有关使用 osext 的答案,因为这是在各种操作系统上与我们所有客户一起工作的实现。我已经使用这种方法实现了代码,但它似乎不是很可靠,许多用户抱怨这种方法为可执行文件选择了错误的路径导致的错误。
  • 在 Windows 上使用 Go 1.6 获得与 emrah 相同的结果(获得临时文件夹的路径而不是源文件夹)。要在不使用任何外部依赖项的情况下获取源文件文件夹的路径,请使用 OP 代码的略微修改版本:_, currentFilePath, _, _ := runtime.Caller(0)dirpath := path.Dir(currentFilePath)(注意 runtime.Caller(0) 而不是 runtime.Caller(1)
【解决方案3】:

使用包osext

它提供了函数ExecutableFolder(),它返回当前运行的程序可执行文件所在文件夹的绝对路径(对cron作业有用)。它是跨平台的。

Online documentation

package main

import (
    "github.com/kardianos/osext"
    "fmt"
    "log"
)

func main() {
    folderPath, err := osext.ExecutableFolder()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(folderPath)
}

【讨论】:

  • 这是在 Windows 和 Linux 上为我产生预期结果的唯一答案。
  • 这可以正常工作,直到您想将它与 go run main.go 一起用于本地开发。不知道如何最好地解决这个问题,而无需每次都事先构建可执行文件。
  • 对不起,我不知道如何使它与go run 一起工作。这个二进制文件每次都放在临时文件夹中。
  • @DerekDowling 的一种方法是首先执行go install,然后运行go build -v *.go && ./main-v 会告诉您正在构建哪些文件。一般来说,如果我已经运行了go install,我发现go rungo build 之间的时间差异是可以容忍的。对于 powershell 上的 windows 用户,命令将为 go build -v {*}.go && ./main.exe
  • 既然这会返回$GOPATH/bin/,为什么不使用$GOPATH/bin/呢?
【解决方案4】:
filepath.Abs("./")

Abs 返回路径的绝对表示。如果路径不是 absolute 会加入当前工作目录转 将其转换为绝对路径。

如评论中所述,这将返回当前处于活动状态的目录。

【讨论】:

  • 这会返回当前目录,而不是当前文件的目录。例如,如果从不同的路径调用可执行文件,这将是不同的。
【解决方案5】:

os.Executable:https://tip.golang.org/pkg/os/#Executable

filepath.EvalSymlinks:https://golang.org/pkg/path/filepath/#EvalSymlinks

完整演示:

package main

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

func main() {
    var dirAbsPath string
    ex, err := os.Executable()
    if err == nil {
        dirAbsPath = filepath.Dir(ex)
        fmt.Println(dirAbsPath)
        return
    }

    exReal, err := filepath.EvalSymlinks(ex)
    if err != nil {
        panic(err)
    }
    dirAbsPath = filepath.Dir(exReal)
    fmt.Println(dirAbsPath)
}

【讨论】:

  • 我很确定这将是当前的答案。
  • 在 Windows 上,这给了我一个 \Local\Temp 目录。
【解决方案6】:

如果你用这种方式:

dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
    log.Fatal(err)
}
fmt.Println(dir)

当您使用 GoLand 等 IDE 运行程序时,您将获得 /tmp 路径,因为可执行文件将保存并从 /tmp 运行

我认为获取当前工作目录或“。”的最佳方式是:

import(
  "os" 
  "fmt"
  "log"
)

func main() {
  dir, err := os.Getwd()
    if err != nil {
        log.Fatal(err)
    }
  fmt.Println(dir)
}

os.Getwd() 函数将返回当前工作目录。 并且全部不使用任何外部库:D

【讨论】:

  • 这是不正确的。这将返回执行进程的用户的工作目录,而不是文件的目录。使用 filepath.abs。
  • 它返回正在运行的可执行文件的工作目录。那么如果你使用像 goland 这样的 IDE,并且构建选项中没有工作目录的配置,那么它将从 /tmp 运行,那么 /tmp 对你有什么用处!??但是如果你使用 os.Getwd() 它返回 .exe 或 elf 可执行文件路径。不是/tmp。
  • @Bit 在这样的 IDE 中使用调试的基本模板,是的,给你,然后点击“编辑配置”并填写“输出目录”,这样你就会看到 'os.Args[0] ' 路径就是你想要的
  • @ϻαϻɾΣɀО-MaMrEzO 是的,对。但是如果你按照我的方式处理它,如果你在另一台计算机上运行代码,你不必在任何新系统中配置它。
【解决方案7】:
dir, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
    }

这是针对 golang 版本的:go version go1.13.7 linux/amd64

为我工作,为go run main.go。如果我运行go build -o fileName,并将最终的可执行文件放在其他文件夹中,那么在运行可执行文件时会给出该路径。

【讨论】:

    【解决方案8】:

    如果您使用 kardianos 的包 osext 并且您需要在本地进行测试,就像 Derek Dowling 评论的那样:

    在您想将它与 go run main.go 一起使用之前,它可以正常工作 地方发展。不知道如何最好地解决这个问题 每次都预先构建一个可执行文件。

    解决方案是制作一个 gorun.exe 实用程序,而不是使用 go run。 gorun.exe 实用程序将使用“go build”编译项目,然后在项目的正常目录中立即运行它。

    我在使用其他编译器时遇到了这个问题,并且发现自己制作了这些实用程序,因为它们没有随编译器一起提供......对于像 C 这样的工具来说尤其神秘,您必须在其中编译和链接然后运行它(工作量太大)。

    如果有人喜欢我对 gorun.exe(或 elf)的想法,我可能会很快将其上传到 github..

    对不起,这个答案是评论,但由于我的声誉还不够大,我无法发表评论。

    或者,可以修改“go run”(如果它还没有此功能)以具有诸如“go run -notemp”之类的参数,以不在临时目录(或类似的目录)中运行程序。但我更喜欢只输入 gorun 或“gor”,因为它比复杂的参数短。 Gorun.exe 或 gor.exe 需要安装在与你的 go 编译器相同的目录中

    实现 gorun.exe(或 gor.exe)将是微不足道的,因为我用其他编译器只用几行代码就完成了......(著名的遗言 ;-)

    【讨论】:

    • 如果您希望它同时使用“运行”和构建的可执行文件,那么只需使用 _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile) 代替
    • @Jocelyn,你的评论太棒了,你应该把它变成一个完整的答案!这当然对我有用——在我自己的设置中,我在 macOS 中有一个环境的本地副本,我主要用它来捕获语法错误(和一些语义错误);然后我将代码同步到在 Ubuntu Linux 下运行的部署服务器,当然环境完全不同......所以确实需要弄清楚文件路径在哪里才能正确加载模板、配置文件、静态文件等...
    【解决方案9】:

    有时这就足够了,第一个参数总是文件路径

    package main
    
    import (
        "fmt"
        "os"
    )
    
    
    func main() {
        fmt.Println(os.Args[0])
    
        // or
        dir, _ := os.Getwd()
        fmt.Println(dir)
    }
    

    【讨论】:

      【解决方案10】:
      推荐的答案 Go Language

      我从 Node.js 来到 Go。在 Go 中相当于 __dirname 的 Node.js 是:

      _, filename, _, ok := runtime.Caller(0)
      if !ok {
          return nil, errors.New("unable to get the current filename")
      }
      dirname := filepath.Dir(file)
      

      此线程中的其他一些提及以及错误的原因:

      • os.Executable() 将为您提供当前运行的可执行文件的文件路径。这相当于 Node.js 中的 process.argv[0]。如果您想获取子包的__dirname,则不是这样。
      • os.Getwd() 将为您提供当前工作目录。这相当于 Node.js 中的 process.cwd()。当您从另一个目录运行程序时,这将是错误的。

      最后,我建议不要为此用例引入第三方软件包。这是一个你可以使用的包:

      package current
      
      // Filename is the __filename equivalent
      func Filename() (string, error) {
          _, filename, _, ok := runtime.Caller(1)
          if !ok {
              return "", errors.New("unable to get the current filename")
          }
          return filename, nil
      }
      
      
      // Dirname is the __dirname equivalent
      func Dirname() (string, error) {
          filename, err := Filename()
          if err != nil {
              return "", err
          }
          return filepath.Dir(filename), nil
      }
      

      请注意,我已将 runtime.Caller(1) 调整为 1,因为我们要获取名为 current.Dirname() 的包的目录,而不是包含 current 包的目录。

      【讨论】:

        【解决方案11】:
        // GetCurrentDir
        func GetCurrentDir() string {
            p, _ := os.Getwd()
            return p
        }
        // GetParentDir
        func GetParentDir(dirctory string) string {
            return substr(dirctory, 0, strings.LastIndex(dirctory, "/"))
        }
        func substr(s string, pos, length int) string {
            runes := []rune(s)
            l := pos + length
            if l > len(runes) {
                l = len(runes)
            }
            return string(runes[pos:l])
        }
        

        【讨论】:

        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
        【解决方案12】:

        这里没有一个答案对我有用,至少在go version go1.16.2 darwin/amd64 上。这是节点中唯一接近 __dirname 功能的东西

        这是由 goland 工程师 Daniil Maslov in the jetbrains forums 发布的

        粘贴在下面以便于阅读:


        诀窍其实很简单,就是获取当前执行并将..添加到项目根目录。

        创建一个新目录和文件,如testing_init.go,内容如下:

        package testing_init
        
        import (
          "os"
          "path"
          "runtime"
        )
        
        func init() {
          _, filename, _, _ := runtime.Caller(0)
          dir := path.Join(path.Dir(filename), "..")
          err := os.Chdir(dir)
          if err != nil {
            panic(err)
          }
        }
        
        

        之后,只需将包导入任何测试文件:

        
        package main_test
        
        import (
          _ "project/testing_init"
        )
        
        

        现在您可以从项目根目录指定路径

        【讨论】:

          【解决方案13】:

          如果您的文件不在main package 中,则上述答案将不起作用 我尝试了不同的方法来查找当前运行文件的目录但失败了。

          最好的答案是在question itself 中,这就是我找到file which is not in the main package 的当前工作目录的方式。

          _, filename, _, _ := runtime.Caller(1)
          pwd := path.Dir(filename)
          

          【讨论】:

            【解决方案14】:

            Gustavo Niemeyer 的回答很棒。 但在 Windows 中,运行时 proc 主要位于另一个目录中,如下所示:

            "C:\Users\XXX\AppData\Local\Temp"
            

            如果您使用相对文件路径,例如"/config/api.yaml",这将使用您的代码所在的项目路径。

            【讨论】:

            • 这不是答案。这只是对别人答案的评论。
            猜你喜欢
            • 2022-06-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-04-17
            • 2011-01-18
            • 2010-10-26
            相关资源
            最近更新 更多