将tilde展开到主目录

luk*_*kad 26 path go

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

我扩展目的地的功能看起来像这样.如果给定的路径是绝对路径,则它什么也不做,否则它将与当前工作目录的相对路径连接起来.

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)
}
Run Code Online (Sandbox Code Playgroud)

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

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

jos*_*hlf 68

Go提供了包os/user,它允许您获取当前用户,以及任何用户的主目录:

usr, _ := user.Current()
dir := usr.HomeDir
Run Code Online (Sandbox Code Playgroud)

然后,使用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:])
}
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以使用`filepath.Join(usr.HomeDir,path [2:])`来确保正确添加目录分隔符,而不是`strings.Replace`.在Windows上,至少HomeDir不会以斜杠终止. (3认同)
  • 还要注意,shell 支持 `~user/foo`,它也指的是 `user` 主目录中的 `foo`。 (2认同)
  • 代码应为`dir +"/"`,否则结果缺少一个`/` (2认同)
  • 这个答案99%正确。但是 `if strings.HasPrefix(path, "~/")` 比 `if path[:2] == "~/"` 更准确。如果路径长度为零或一个字符,则后者将失败。路径“~”也没有正确处理——它也应该扩展到 $HOME。 (2认同)

Ana*_*dis 12

通常,进入程序之前,~shell 会进行扩展.但是有一些限制.

一般来说,在Go中手动执行此操作不明智的.

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


zzz*_*zzz 6

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

其中一个可能的问题是使用exec.Command,如下所示:

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

哪个不会扩大.例如,您可以使用:

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

这将从~shell 获得标准扩展.

编辑:修复'sh -c'示例.


小智 5

如果要扩展波浪号“〜”以供使用,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)
}
Run Code Online (Sandbox Code Playgroud)

然而; 加载应用程序配置文件(例如~./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
}
Run Code Online (Sandbox Code Playgroud)

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


and*_*shu 5

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

myPath := "~/.ssh"
fmt.Printf("path: %s; with expansion: %s", myPath, homedir.Expand(myPath))
Run Code Online (Sandbox Code Playgroud)