How to get the directory of the currently running file?

huangapple go评论92阅读模式
英文:

How to get the directory of the currently running file?

问题

在Node.js中,我使用__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中正确的方式或惯用的方式吗?

英文:

In nodejs I use __dirname . What is the equivalent of this in Golang?

I have googled and found out this article http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Where he uses below code

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

But is it the right way or idiomatic way to do in Golang?

答案1

得分: 418

编辑:从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)
}
英文:

EDIT: As of Go 1.8 (Released February 2017) the recommended way of doing this is with os.Executable:

>func Executable() (string, error)
>
>Executable returns the path name for the executable that started the current process. There is no guarantee that the path is still pointing to the correct executable. If a symlink was used to start the process, depending on the operating system, the result might be the symlink or the path it pointed to. If a stable result is needed, path/filepath.EvalSymlinks might help.

To get just the directory of the executable you can use 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)
}

OLD ANSWER:

You should be able to use os.Getwd

func Getwd() (pwd string, err error)

>Getwd returns a rooted path name corresponding to the current directory. If the current directory can be reached via multiple paths (due to symbolic links), Getwd may return any one of them.

For example:

package main

import (
    "fmt"
    "os"
)

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

答案2

得分: 259

这应该可以:

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

请注意,这是一个Go语言的代码示例,用于获取当前执行文件的绝对路径并打印出来。

英文:

This should do it:

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

答案3

得分: 71

使用包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)
}
英文:

Use package osext

It's providing function ExecutableFolder() that returns an absolute path to folder where the currently running program executable reside (useful for cron jobs). It's cross platform.

Online documentation

<!-- language: go -->

package main

import (
	&quot;github.com/kardianos/osext&quot;
	&quot;fmt&quot;
	&quot;log&quot;
)

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

答案4

得分: 37

我从Node.js转到了Go。在Go中,__dirname的等效方式是:

_, filename, _, ok := runtime.Caller(0)
if !ok {
	return errors.New("无法获取当前文件名")
}
dirname := filepath.Dir(filename)

这个线程中提到的其他方法以及它们为什么是错误的:

  • os.Executable()会给出当前正在运行的可执行文件的文件路径。这相当于Node.js中的process.argv[0]。但是,如果你想获取子包的__dirname,这种方法是不正确的。
  • os.Getwd()会给出当前的工作目录。这相当于Node.js中的process.cwd()。但是,如果你从另一个目录运行程序,这种方法是错误的。

最后,我建议不要为这种情况引入第三方包。这里有一个你可以使用的包:

package current

// Filename是__filename的等效方式
func Filename() (string, error) {
	_, filename, _, ok := runtime.Caller(1)
	if !ok {
		return "", errors.New("无法获取当前文件名")
	}
	return filename, nil
}


// Dirname是__dirname的等效方式
func Dirname() (string, error) {
	filename, err := Filename()
	if err != nil {
		return "", err
	}
	return filepath.Dir(filename), nil
}

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

英文:

I came from Node.js to Go. The Node.js equivalent to __dirname in Go is:

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

Some other mentions in this thread and why they're wrong:

  • os.Executable() will give you the filepath of the currently running executable. This is equivalent to process.argv[0] in Node. This is not true if you want to take the __dirname of a sub-package.
  • os.Getwd() will give you the current working directory. This is the equivalent to process.cwd() in Node. This will be wrong when you run your program from another directory.

Lastly, I'd recommend against pulling in a third-party package for this use case. Here's a package you can use:

package current

// Filename is the __filename equivalent
func Filename() (string, error) {
	_, filename, _, ok := runtime.Caller(1)
	if !ok {
		return &quot;&quot;, errors.New(&quot;unable to get the current filename&quot;)
	}
	return filename, nil
}


// Dirname is the __dirname equivalent
func Dirname() (string, error) {
	filename, err := Filename()
	if err != nil {
		return &quot;&quot;, err
	}
	return filepath.Dir(filename), nil
}

Note that I've adjusted runtime.Caller(1) to 1 because we want to get the directory of the package that called current.Dirname(), not the directory containing the current package.

答案5

得分: 13

如果你像这样做:

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() 函数将返回当前工作目录。

英文:

if you do it like this :

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

you will get the /tmp path when you are running program using some IDE like GoLand because the executable will be saved and run from /tmp

i think the best way for getting the currentWorking Directory or '.' is :

import(
  &quot;os&quot; 
  &quot;fmt&quot;
  &quot;log&quot;
)

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

the os.Getwd() function will return the current working directory.

答案6

得分: 12

os.Executable函数返回当前可执行文件的路径。你可以在这里找到更多关于os.Executable的信息:https://tip.golang.org/pkg/os/#Executable

filepath.EvalSymlinks函数返回符号链接路径的绝对路径。你可以在这里找到更多关于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)
}

希望对你有帮助!

英文:

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

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

Full Demo:

package main

import (
	&quot;fmt&quot;
	&quot;os&quot;
	&quot;path/filepath&quot;
)

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

答案7

得分: 11

filepath.Abs("./")

Abs返回路径的绝对表示。如果路径不是绝对路径,则会与当前工作目录连接,将其转换为绝对路径。

正如注释中所述,这将返回当前活动的目录。

英文:
filepath.Abs(&quot;./&quot;)

> Abs returns an absolute representation of path. If the path is not
> absolute it will be joined with the current working directory to turn
> it into an absolute path.

As stated in the comment, this returns the directory which is currently active.

答案8

得分: 6

不要使用runtime.Caller(0)与"Go语言推荐的答案"。

当你使用go buildgo install编译程序时,这是有效的,因为你正在重新编译它。

但是,当你go build一个程序然后分发它(复制)到你的同事的工作站上(他们没有安装Go,只需要可执行文件),runtime.Caller(0)的结果仍然是你构建程序的路径(来自你的计算机)。
也就是说,这个路径在他们自己的计算机上很可能不存在

os.Args[0]或者更好的是os.Executable()(在这里提到)和kardianos/osext(在这里这里提到)更可靠。

英文:

Do not use the "Answer recommended by Go Language" with runtime.Caller(0).

That works when you go build or go install a program, because you are re-compiling it.

But when you go build a program and then distribute it (copy) on your colleagues' workstations (who don't have Go, and just need the executable), the result of runtime.Caller(0) would still be the path of where you built it (from your computer).
Ie a path which would likely not exist on their own computer.

os.Args[0] or, better, os.Executable() (mentioned here) and kardianos/osext (mentioned here and here), are more reliable.

答案9

得分: 5

如果你使用kardianososext包,并且需要在本地进行测试,就像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)将是很简单的,因为我在其他编译器上只用了几行代码就完成了...(著名的最后一句话;-)

英文:

If you use package osext by kardianos and you need to test locally, like Derek Dowling commented:

> This works fine until you'd like to use it with go run main.go for
> local development. Not sure how best to get around that without
> building an executable beforehand each time.

The solution to this is to make a gorun.exe utility instead of using go run. The gorun.exe utility would compile the project using "go build", then run it right after, in the normal directory of your project.

I had this issue with other compilers and found myself making these utilities since they are not shipped with the compiler... it is especially arcane with tools like C where you have to compile and link and then run it (too much work).

If anyone likes my idea of gorun.exe (or elf) I will likely upload it to github soon..

Sorry, this answer is meant as a comment, but I cannot comment due to me not having a reputation big enough yet.

Alternatively, "go run" could be modified (if it does not have this feature already) to have a parameter such as "go run -notemp" to not run the program in a temporary directory (or something similar). But I would prefer just typing out gorun or "gor" as it is shorter than a convoluted parameter. Gorun.exe or gor.exe would need to be installed in the same directory as your go compiler

Implementing gorun.exe (or gor.exe) would be trivial, as I have done it with other compilers in only a few lines of code... (famous last words How to get the directory of the currently running file?

答案10

得分: 5

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

package main

import (
	"fmt"
	"os"
)

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

    // 或者
    dir, _ := os.Getwd()
    fmt.Println(dir)
}
英文:

Sometimes this is enough, the first argument will always be the file path

package main

import (
	&quot;fmt&quot;
	&quot;os&quot;
)


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

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

答案11

得分: 5

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,并将最终的可执行文件放在其他文件夹中,那么在运行可执行文件时会给出该路径。


<details>
<summary>英文:</summary>

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


this is for golang version: `go version go1.13.7 linux/amd64`

works for me, for `go run main.go`. If I run `go build -o fileName`, and put the final executable in some other folder, then that path is given while running the executable.

</details>



# 答案12
**得分**: 0

这里的所有答案对我都没有用,至少在`go version go1.16.2 darwin/amd64`上是这样的。这是在node中最接近`__dirname`功能的方法。

这是由goland工程师[Daniil Maslov在jetbrains论坛上发布的](https://intellij-support.jetbrains.com/hc/en-us/community/posts/360009685279/comments/360002183640)。

以下是为了更容易阅读而粘贴的内容:

----------
这个技巧实际上非常简单,就是获取当前执行的目录并在项目根目录中添加`..`。

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

```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"
)

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

英文:

None of the answers here worked for me, at least on go version go1.16.2 darwin/amd64. This is the only thing close to the __dirname functionality in node

This was posted by goland engineer Daniil Maslov in the jetbrains forums

pasted below for easier reading:


The trick is actually very simple and is to get the current executing and add .. to the project root.

Create a new directory and file like testing_init.go with the following content:

package testing_init

import (
  &quot;os&quot;
  &quot;path&quot;
  &quot;runtime&quot;
)

func init() {
  _, filename, _, _ := runtime.Caller(0)
  dir := path.Join(path.Dir(filename), &quot;..&quot;)
  err := os.Chdir(dir)
  if err != nil {
    panic(err)
  }
}

After that, just import the package into any of the test files:


package main_test

import (
  _ &quot;project/testing_init&quot;
)

Now you can specify paths from the project root

答案13

得分: 0

我从日志包中获取信息(如果你还需要调用它的行号)

log.Lshortfile
英文:

I get the information from the log package (if you also need the linenumber where its called)

log.Lshortfile

答案14

得分: 0

以下方法检查您是否在本地机器上使用go run .,如果是,则跳过更改目录。在实践中,这可能对您的用例足够好。我应该指出,如果您进入子目录并运行go mod ..,您的工作目录将不同。

以下假设您正在运行Linux。其他操作系统可能需要不同的路径比较。

package main

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

func main() {

    // /tmp/ is an os-specific dir holding executables compiled by `go run`
    matched, err := regexp.MatchString("^/tmp/", os.Args[0])

    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }

    if !matched {
        // if we're in this block, we're running a pre-compiled binary
        newDir := filepath.Dir(os.Args[0])

        fmt.Printf("changing dir to: %s\n", newDir)
        os.Chdir(newDir)

    } else {
        // we're running with something like `go run .`
        fmt.Println("not changing dir")
    }
}
英文:

The following method checks if you might be using go run . on your local machine, and if so, it skips changing the directory. In practice, this may be good enough for your use case. I should note that if you cd into a subdirectory and run go mod .., your working directory will differ.

The following presumes that you're running Linux. Other OSes may require different path comparisons.

package main

import (
    &quot;os&quot;
    &quot;fmt&quot;
    &quot;regexp&quot;
    &quot;path/filepath&quot;
)

func main() {

    // /tmp/ is an os-specific dir holding executables compiled by `go run`
    matched, err := regexp.MatchString(&quot;^/tmp/&quot;, os.Args[0])

    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }

    if !matched {
        // if we&#39;re in this block, we&#39;re running a pre-compiled binary
        newDir := filepath.Dir(os.Args[0])

        fmt.Printf(&quot;changing dir to: %s\n&quot;, newDir)
        os.Chdir(newDir)

    } else {
        // we&#39;re running with something like `go run .`
        fmt.Println(&quot;not changing dir&quot;)
    }
}

答案15

得分: -1

// 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])
}
// 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])
}
英文:
// GetCurrentDir
func GetCurrentDir() string {
	p, _ := os.Getwd()
	return p
}
// GetParentDir
func GetParentDir(dirctory string) string {
	return substr(dirctory, 0, strings.LastIndex(dirctory, &quot;/&quot;))
}
func substr(s string, pos, length int) string {
	runes := []rune(s)
	l := pos + length
	if l &gt; len(runes) {
		l = len(runes)
	}
	return string(runes[pos:l])
}

答案16

得分: -1

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

最好的答案在“问题本身”中,这是我找到“不在主包中的文件”的当前工作目录的方法。

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

If your file is not in the main package then the above answers won't work
I tried different approaches to find find the directory of the currently running file but failed.

The best possible answer is in the question itself this is how I find the current working directory of the file which is not in the main package.

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

答案17

得分: -5

Gustavo Niemeyer的回答很好。
但在Windows中,运行时进程大多在另一个目录中,就像这样:

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

如果你使用相对文件路径,比如"/config/api.yaml",这将使用你的项目路径,即你的代码所在的位置。

英文:

Gustavo Niemeyer's answer is great.
But in Windows, runtime proc is mostly in another dir, like this:

&quot;C:\Users\XXX\AppData\Local\Temp&quot;

If you use relative file path, like &quot;/config/api.yaml&quot;, this will use your project path where your code exists.

huangapple
  • 本文由 发表于 2013年8月31日 00:07:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/18537257.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定