47

我有一个程序接受将在其中创建文件的目标文件夹。我的程序应该能够处理绝对路径和相对路径。我的问题是我不知道如何扩展~到主目录。

我扩展目的地的功能如下所示。如果给定的路径是绝对路径,则它什么也不做,否则它将相对路径与当前工作目录连接。

import "path"
import "os"

// var destination *String is the user input

func expandPath() {
        if path.IsAbs(*destination) {
                return
        }
        cwd, err := os.Getwd()
        checkError(err)
        *destination = path.Join(cwd, *destination)
}

由于path.Join不展开,如果用户传递类似目的地的~东西,它就不起作用。~/Downloads

我应该如何以跨平台的方式解决这个问题?

4

5 回答 5

99

Go 提供了包os/user,它允许你获取当前用户,对于任何用户,他们的主目录:

usr, _ := user.Current()
dir := usr.HomeDir

然后,使用path/filepath将两个字符串组合成一个有效路径:

if path == "~" {
    // In case of "~", which won't be caught by the "else if"
    path = dir
} else if strings.HasPrefix(path, "~/") {
    // Use strings.HasPrefix so we don't match paths like
    // "/something/~/something/"
    path = filepath.Join(dir, path[2:])
}

(注意 user.Current() 没有在 go playground 中实现(可能是出于安全原因),所以我不能给出一个容易运行的例子)。

于 2013-07-12T14:49:29.607 回答
18

通常,在它进入您的程序之前~,它会被您的 shell 扩展。但也有一些限制

一般来说,不建议在 Go 中手动进行

我在我的一个程序中遇到了同样的问题,我的理解是,如果我使用标志格式 as --flag=~/myfile,它不会被扩展。但是,如果您运行--flag ~/myfile它,它会被 shell 扩展(=缺少并且文件名显示为单独的“单词”)。

于 2015-05-04T10:08:06.197 回答
8

通常,在您的程序看到它之前~,shell 会扩展它。 以与 shell 扩展机制兼容的方式调整程序从命令行获取参数的方式。

可能的问题之一是像这样使用exec.Command

cmd := exec.Command("some-binary", someArg) // say 'someArg' is "~/foo"

这不会得到扩展。例如,您可以改用:

cmd := exec.Command("sh", "-c", fmt.Sprintf("'some-binary %q'", someArg))

这将从外壳中获得标准~扩展。

编辑:修复了“sh -c”示例。

于 2013-07-12T07:39:43.800 回答
7

如果您要扩展波浪号“~”以供您使用,exec.Command()则应使用用户本地 shell 进行扩展。

// 'sh', 'bash' and 'zsh' all respect the '-c' argument
cmd := exec.Command(os.Getenv("SHELL"), "-c", "cat ~/.myrc")
cmd.Stdout = os.Stdout
if err := cmd.Run(); err != nil {
    fmt.Fprintln(os.Stderr, err)
}

然而; 加载应用程序配置文件时,例如~./myrc此解决方案是不可接受的。以下在多个平台上对我来说效果很好

import "os/user"
import "path/filepath"

func expand(path string) (string, error) {
    if len(path) == 0 || path[0] != '~' {
        return path, nil
    }

    usr, err := user.Current()
    if err != nil {
        return "", err
    }
    return filepath.Join(usr.HomeDir, path[1:]), nil
}

注意: usr.HomeDir不尊重而是通过(osx/linux)上的系统调用读取文件$HOME来确定主目录。在 Windows 上,它使用系统调用来确定用户的主目录。/etc/passwdgetpwuid_rOpenCurrentProcessToken

于 2017-04-24T01:33:15.030 回答
6

我知道这是一个老问题,但现在有另一种选择。您可以使用go-homedir将 tidle 扩展到用户的 homedir:

myPath := "~/.ssh"
fmt.Printf("path: %s; with expansion: %s", myPath, homedir.Expand(myPath))
于 2017-09-18T18:56:27.247 回答