如何从代码中获取当前的GOPATH

22

如何从一段代码中获取当前的 GOPATH

runtime 只有 GOROOT

// GOROOT returns the root of the Go tree.
// It uses the GOROOT environment variable, if set,
// or else the root used during the Go build.
func GOROOT() string {
    s := gogetenv("GOROOT")
    if s != "" {
        return s
    }
    return defaultGoroot
}

我可以编写一个函数来将GOROOT替换为GOPATH,但是是否有内置的函数可以实现这个功能呢?

4个回答

33

使用os.Getenv

来自文档:

Getenv 检索由键指定的环境变量的值。如果变量不存在,它将返回空字符串。

示例:

package main

import (
    "fmt"
    "os"
    )

func main() {
    fmt.Println(os.Getenv("GOPATH"))
}

Go 1.8+ 更新说明

Go 1.8 已通过 go/build 导出默认的 GOPATH:

package main

import (
    "fmt"
    "go/build"
    "os"
)

func main() {
    gopath := os.Getenv("GOPATH")
    if gopath == "" {
        gopath = build.Default.GOPATH
    }
    fmt.Println(gopath)
}

7
在 Go 1.8 中,GOPATH 环境变量是可选的。如果用户没有设置它,是否有一种方法可以获取默认值?我认为 Go 运行时应该有一种方法来获取 GOPATH,让 Go 自己为你解决这个问题。 - tothemario
@tothemario,我发布了一个答案,告诉你如何在Go 1.8及以上版本中获取有效的GOPATH。然而,我同意理想情况下运行时应该提供一种获取用户GOPATH的方法。 - tmh
1
@tothemario 更新了答案,使用了 Go 1.8 默认的 go path 实现中的代码。 - codefreak
1
不要忘记,GOPATH 可能会列出多个路径,并由操作系统的路径分隔符分隔。 - Benny Jobigan

14

你应该使用go/build包。

package main

import (
    "fmt"
    "go/build"
)

func main() {
    fmt.Println(build.Default.GOPATH)
}

1
我今天在处理这个问题,因为我正在开发一个项目,结果比我预期的更加烦人。最终,在我进行的各种测试中,这似乎对我有用(不是“严格”的测试)。
goPath := strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator))
if len(goPath) == 0 {
    goPath = append(goPath, build.Default.GOPATH)
} else if goPath[0] == "" {
    goPath[0] = build.Default.GOPATH
}

请注意,如果GOPATH中列出了多个路径,则我决定仅使用第一个路径,因为我怀疑很少有人会列出多个路径,而第一个路径应该是go get放置源代码的位置(我猜测)。此代码还未考虑路径是否有效。
另请注意,在获取GOPATH后构建文件路径时,我必须使用path/filepath.Join()而不是path.Join()。前者将在Windows上使用\(如果第一个参数包含\),并对其他系统使用/。尽管Windows显然可以接受/作为路径,但我所有类似于PATH和GOPATH的环境变量都使用了Windows的正常\。而path.Join()则使用/,产生无效的路径。

0
自Go 1.8以来,有一个默认GOPATH

GOPATH环境变量指定了您的工作区位置。它默认为名为go的目录,位于您的主目录内,因此在Unix上为$HOME/go,在Plan 9上为$home/go,在Windows上为%USERPROFILE%\go(通常为C:\Users\YourName\go)。

这意味着GOPATH环境变量不一定设置为特定值。
您仍然可以使用os.Getenv获取该值,但如果未设置,则需要确保您使用平台特定的默认值,例如使用mitchellh/go-homedir
package main

import (
        "fmt"
        "log"
        "os"

        "github.com/mitchellh/go-homedir"
)

func main() {
        p, err := gopath()
        if err != nil {
                log.Fatalf("Error finding GOPATH: %v", err)
        }

        fmt.Println(p)
}

func gopath() (string, error) {
        s := os.Getenv("GOPATH")
        if s != "" {
                return s, nil
        }
        return homedir.Expand("~/go")
}

1
这有点过头了。我们可以使用Go自己的源代码中的代码来获取默认的go路径。请参见https://github.com/golang/go/blob/go1.8/src/go/build/build.go#L260-L277 - codefreak
@codefreak 当然有更简单的方法,你的答案也是有效的。我喜欢go-homedir的原因是,在怀疑时,它尝试多种方式查找主目录,如果所有方式都失败了,它会返回一个错误。因此,这个代码的用户可以返回一个有意义的错误消息。如果你只返回一个空字符串,用户必须检查它是否为空。如果他们忽略了这一点,这可能会导致一些边缘情况下的意外行为或奇怪的错误。 - tmh
1
问题的重点是找到GOPATH,而不是主目录。go-homedir非常适合查找主目录。但是如果Go本身使用defaultGOPATH函数,那么照顾Go构建代码本身不照顾的边缘情况有什么意义呢?无论如何,让我们让用户自己决定他们最喜欢什么 :) - codefreak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接