在 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 中的正确方式还是惯用方式?
0
而不是 1
传递给 runtime.Caller()
。
runtime.Caller(0)
将为您提供源文件的路径,如 $GOPATH/src/packagename/main.go
。该线程中的其他答案试图返回可执行文件的路径(如 $GOPATH/bin/packagename
)。
编辑:从 Go 1.8(2017 年 2 月发布)开始,推荐的方法是使用 os.Executable
:
func Executable() (string, error) Executable 返回启动当前进程的可执行文件的路径名。不能保证路径仍然指向正确的可执行文件。如果使用符号链接来启动进程,则取决于操作系统,结果可能是符号链接或它指向的路径。如果需要稳定的结果, 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)
}
这应该这样做:
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)
}
_, currentFilePath, _, _ := runtime.Caller(0)
dirpath := path.Dir(currentFilePath)
(注意 runtime.Caller(0)
而不是 runtime.Caller(1)
)
使用包 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)
}
go run main.go
一起用于本地开发之前,这可以正常工作。不知道如何最好地解决这个问题,而无需每次都事先构建可执行文件。
go run
一起使用。此二进制文件每次都放在临时文件夹中。
go install
,然后运行 go build -v *.go && ./main
。 -v
会告诉您正在构建哪些文件。一般来说,如果我已经运行 go install
,我发现 go run
和 go build
之间的时间差异是可以容忍的。对于 powershell 上的 windows 用户,命令将为 go build -v {*}.go && ./main.exe
$GOPATH/bin/
,为什么不使用 $GOPATH/bin/
?
filepath.Abs("./")
Abs 返回路径的绝对表示。如果路径不是绝对路径,它将与当前工作目录连接以将其转换为绝对路径。
如评论中所述,这将返回当前处于活动状态的目录。
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)
}
我从 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 中的 process.argv[0]。如果您想获取子包的 __dirname,则不是这样。
os.Getwd() 将为您提供当前工作目录。这相当于 Node 中的 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
包的目录。
如果您使用这种方式:
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
如果您使用 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)
有时这就足够了,第一个参数将始终是文件路径
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println(os.Args[0])
// or
dir, _ := os.Getwd()
fmt.Println(dir)
}
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
,并将最终的可执行文件放在其他文件夹中,则在运行可执行文件时会给出该路径。
不要将“Answer recommended by Go Language”与 runtime.Caller(0)
一起使用。
这在您go build
或go install
程序时有效,因为您正在重新编译它。
但是,当您go build
一个程序然后将其分发(副本)到您同事的工作站(他们没有 Go,只需要可执行文件)时,runtime.Caller(0)
的结果将 < em>仍然是您构建它的路径(从您的计算机)。
即可能在他们自己的计算机上存在的路径。
os.Args[0]
或更好的 os.Executable()
(提到 here)和 kardianos/osext
(提到 here 和 here)更可靠。
这里没有一个答案对我有用,至少在 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"
)
现在您可以从项目根目录指定路径
如果您的文件不在 main package
中,那么上述答案将不起作用我尝试了不同的方法来查找当前正在运行的文件的目录但失败了。
最好的答案是在 question itself
中,这就是我找到 file which is not in the main package
的当前工作目录的方式。
_, filename, _, _ := runtime.Caller(1)
pwd := path.Dir(filename)
// 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])
}
Gustavo Niemeyer 的回答很棒。但在 Windows 中,运行时 proc 主要位于另一个目录中,如下所示:
"C:\Users\XXX\AppData\Local\Temp"
如果您使用相对文件路径,例如 "/config/api.yaml"
,这将使用您的代码所在的项目路径。
runtime.Caller
是最接近“惯用”的path/filepath.Dir
进行编辑,因为path.Dir
仅适用于正斜杠(Unix 样式)作为目录分隔符。