将tilde展开到主目录

时间:2013-07-12 07:30:12

标签: 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)
}

由于path.Join没有展开~,如果用户通过~/Downloads之类的内容作为目的地,它就不起作用。

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

5 个答案:

答案 0 :(得分:64)

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

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

答案 1 :(得分:11)

一般情况下,{<1}}会在到达您的程序之前由您的shell 进行扩展。但有some limitations

一般来说是ill-advised to do it manually in Go

我在我的程序中遇到了同样的问题,我所理解的是,如果我将标志格式用作~,则不会扩展。但是如果你运行--flag=~/myfile它会被shell扩展(--flag ~/myfile丢失,文件名显示为单独的&#34;字&#34;)。

答案 2 :(得分:6)

通常情况下,{<1}}会在程序看到之前由shell 扩展。
调整程序以与shell扩展机制兼容的方式从命令行获取其参数的方式。

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

~

不会扩展。例如,您可以使用:

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

将从shell获得标准cmd := exec.Command("sh", "-c", fmt.Sprintf("'some-binary %q'", someArg)) 扩展。

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

答案 3 :(得分:5)

如果你正在扩展代字号&#39;〜&#39;与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不尊重$HOME而是通过/etc/passwd系统调用(osx)读取getpwuid_r文件来确定主目录/ Linux的)。在Windows上,它使用OpenCurrentProcessToken系统调用来确定用户主目录。

答案 4 :(得分:4)

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

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