如何获取当前运行文件的目录?


239

在nodejs中,我使用__dirname。在Golang中这等效吗?

我已经在Google上搜索并找到了这篇文章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
oguzalb

你应该通过0,不1,要runtime.Caller()
fiatjaf

4
runtime.Caller(0)将为您提供源文件的路径,例如$GOPATH/src/packagename/main.go。该线程中的其他答案正在尝试返回可执行文件的路径(如$GOPATH/bin/packagename)。
fiatjaf

您假设程序正在从文件中运行...
Flimzy

Answers:


221

应该这样做:

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)
}

2
这里可能有错误吗?如果是这样,只是出于好奇,会是什么错误?
Jeff Escalante 2014年

4
不适用于我play.golang.org/p/c8fe-Zm_bH-os.Args [0]不一定包含abs路径。
zupa

5
即使os.Args [0]不包含abs路径,它实际上也可以工作。游乐场结果与预期不符的原因是,它位于沙箱中。
古斯塔沃·尼迈耶

37
这不是一种可靠的方法,请参见有关使用osext的答案,因为这是与我们所有客户端在各种OS上一起使用的实现。我已经使用这种方法实现了代码,但是它似乎不是很可靠,许多用户抱怨这种方法为可执行文件选择了错误的路径而导致的错误。
JD D

5
在Windows上使用Go 1.6获得了与emrah相同的结果(获得了临时文件夹而不是源文件文件夹的路径)。要在不使用任何外部依赖关系的情况下获取源文件文件夹的路径,请使用经过精心修改的OP代码:(_, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)请注意,runtime.Caller(0)而不是runtime.Caller(1)
TanguyP

295

编辑:从Go 1.8(2017年2月发布)开始,推荐的方法是os.Executable

func Executable() (string, error)

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

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

范例

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)
}

3
这是当前进程的工作目录。在nodejs中,它等效于process.cwd()nodejs.org/api/process.html#process_process_cwd
ekanna 2013年

2
好的,我看到了区别。你们中的二进制文件位于文件系统(而不是当前工作目录)之后,我认为这runtime.Caller是您最接近“惯用语言”的地方
Intermernet

3
“ 2017年2月发布”?似乎已经发明了时间机器,并且我们有一些成员在将来发布。很高兴知道将来的版本将具有可靠的跨平台方法,与此同时,我们必须坚持目前可用的解决方案。
ljgww

1
@ljgww对不起,我带上我的Delorean回家:-)我提前更新了答案,因为我只是看到了即将发布的功能,以为我忘了以后再更新答案。
Intermernet

1
path/filepath.Dir之所以进行编辑,是因为path.Dir仅将正斜杠(Unix样式)用作目录分隔符。
乔斯林

63

使用软件包osext

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

在线文件

package main

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

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

13
这是在Windows和Linux上都能为我带来预期结果的唯一答案。
DannyB 2014年

3
直到您希望将其go run main.go用于本地开发之前,它都可以正常工作。不知道如何最好地解决此问题,而无需每次都事先构建可执行文件。
Derek Dowling

1
抱歉,我不知道如何使用它go run。每次将此二进制文件放入临时文件夹中。
DobrosławŻybort

2
@DerekDowling一种方法是先做一个go install,然后运行go build -v *.go && ./main。该-v会告诉您哪些文件正在兴建。一般情况下,我发现之间的时间不同go run,并go build是可容忍的,如果我已经跑go install。对于go build -v {*}.go && ./main.exe
Powershell

既然会回来$GOPATH/bin/,为什么不使用$GOPATH/bin/呢?
fiatjaf

10
filepath.Abs("./")

Abs返回路径的绝对表示。如果该路径不是绝对路径,它将与当前工作目录合并以将其变为绝对路径。

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


8
这将返回当前目录,而不是当前文件的目录。例如,如果从不同路径调用可执行文件,则情况将有所不同。
藤井

8

如果您使用这种方式:

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

使用诸如GoLang之类的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。
PodTech.io

1
它返回正在运行的可执行文件的工作目录。然后,如果您使用的是goland这样的IDE,并且在构建选项中没有用于工作目录的配置,那么它将从/ tmp运行,那么/ tmp有什么用处!?但是如果您使用os.Getwd()返回.exe或elf可执行文件的路径。不是/ tmp。

@Bit在这样的IDE中使用调试的基本模板,是的,您只需单击“编辑配置”并填写“输出目录”,即可看到所需的“ os.Args [0]”路径
–ϻα

5

如果您使用kardianos的osext软件包,并且需要在本地进行测试,例如Derek Dowling评论:

在您希望将其与go run main.go一起用于本地开发之前,此方法可以正常工作。不知道如何最好地解决此问题,而无需每次都事先构建可执行文件。

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

我在其他编译器中遇到了这个问题,发现自己制造了这些实用程序,因为它们没有随编译器一起提供...尤其是在使用诸如C之类的工具时,您必须进行编译和链接然后运行它(工作量过多),这非常不可思议。

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

抱歉,此答案仅作为评论,但由于我的声誉还不够高,所以我无法发表评论。

或者,可以修改“ go run”(如果尚未具有此功能)以具有诸如“ go run -notemp”之类的参数,以不在临时目录(或类似目录)中运行程序。但是我宁愿只键入gorun或“ gor”,因为它比卷积参数短。Gorun.exe或gor.exe将需要与go编译器安装在同一目录中

实现gorun.exe(或gor.exe)将是微不足道的,因为我仅用几行代码就与其他编译器一起完成了……(著名的最后一句话;-)


3
如果您希望它既可以“运行”又可以构建可执行文件,则只需_, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)改用
Jocelyn

@Jocelyn,您的评论太好了,您应该对它做出完整的回答!这肯定对我有用。在我自己的设置中,我在macOS中拥有该环境的本地副本,主要用于捕获语法错误(以及一些语义错误)。然后我将代码同步到在Ubuntu Linux下运行的部署服务器,当然环境是完全不同的...因此,真正需要弄清楚文件路径在哪里正确加载模板,配置文件,静态文件。文件等...
Gwyneth Llewelyn


4

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

package main

import (
    "fmt"
    "os"
)


func main() {
    fmt.Println(os.Args[0])

    // or
    dir, _ := os.Getwd()
    fmt.Println(dir)
}

0

古斯塔沃·尼迈耶(Gustavo Niemeyer)的回答很好。但是在Windows中,运行时proc主要位于另一个目录中,如下所示:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.