Path 将平铺展开到主目录

Path 将平铺展开到主目录,path,go,Path,Go,我有一个程序,它接受一个目标文件夹,文件将在其中创建。我的程序应该能够处理绝对路径和相对路径。我的问题是我不知道如何将~扩展到主目录 import "path" import "os" // var destination *String is the user input func expandPath() { if path.IsAbs(*destination) { return } cwd, err :=

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

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)
}
我扩展目标的函数如下所示。如果给定的路径是绝对路径,则不会执行任何操作,否则它会将相对路径与当前工作目录联接

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)
}
因为它不展开
~
,如果用户将
~/Downloads
之类的内容作为目标传递,它就不起作用


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

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

其中一个可能的问题是这样使用:

cmd := exec.Command("some-binary", someArg) // say 'someArg' is "~/foo"
它不会被扩展。例如,您可以改为使用:

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

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

Go提供了一个包,允许您获取当前用户,对于任何用户,都可以获取其主目录:

usr, _ := user.Current()
dir := usr.HomeDir
然后,使用将两个字符串组合到有效路径:

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游乐场中实现的(可能是出于安全原因),因此我无法给出一个易于运行的示例)。

一般来说,
~
在进入程序之前由shell进行扩展。但是有

一般来说是这样


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

如果要展开tilde'~”以与
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
而是通过系统调用(osx/linux)读取
/etc/passwd
文件来确定主目录。在windows上,它使用
OpenCurrentProcessToken
syscall来确定用户的主目录。

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

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)
}
myPath := "~/.ssh"
fmt.Printf("path: %s; with expansion: %s", myPath, homedir.Expand(myPath))

谢谢,我不知道shell会这样做。@jnml,我想说格式字符串中的第一个单引号放错了位置,应该放在
%q
占位符前面,以便读取
fmt.Sprintf(“某个二进制“%q”,someArg)
@kostix:我不这么认为。在上面的示例中,从Sprintf返回的值应该是
'some-binary”/home/login/foo“
。没有测试过,也许外部的单引号应该被删除。但是,在命令行中它们是正确的。@jml,是的,您是正确的:
%q
本身生成一个带引号的字符串(我没有看到)。但我认为我最初的想法仍然成立:调用
fmt.Sprintf
生成的任何字符串都将是一个参数,因此无需进一步引用它。如果您将路径作为CLI参数,但如果您从其他任何地方获得路径字符串,则不会。还要注意,shell支持
~user/foo
,它也引用
用户的主目录中的
foo
。代码应为
dir+“/”
,否则,结果将缺少一个
/
而不是
字符串。替换
您可以使用
filepath.Join(usr.HomeDir,path[2:])
来确保正确添加了目录分隔符。在Windows上,至少HomeDir没有以斜杠结尾。这个答案是99%正确的。但是
if-strings.HasPrefix(path,“~/”)比
if-path[:2]==“~/”
更准确。如果路径长度为零或一个字符,则后者将失败。路径“~”也没有正确处理——它还应该扩展到$HOME.Good catch;修正了,太棒了!感谢您提供了一个完整的示例(包括错误检查和所有!),但是您还需要
导入“path/filepath”
,否则您的代码将无法工作。这比公认的答案或投票最多的答案更简单、更正确。