我有一个程序接受将创建文件的目标文件夹.我的程序应该能够处理绝对路径以及相对路径.我的问题是我不知道如何扩展~到主目录.
我扩展目的地的功能看起来像这样.如果给定的路径是绝对路径,则它什么也不做,否则它将与当前工作目录的相对路径连接起来.
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操场中实现(可能出于安全原因),所以我不能给出一个易于运行的例子).
Ana*_*dis 12
通常,在进入程序之前,~shell 会进行扩展.但是有一些限制.
一般来说,在Go中手动执行此操作是不明智的.
我在我的程序中遇到了同样的问题,我所理解的是,如果我使用标志格式--flag=~/myfile,则不会扩展.但是如果你运行--flag ~/myfile它会被shell扩展(=缺少并且文件名显示为单独的"单词").
通常,在程序看到~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来确定用户的主目录。
我知道这是一个老问题,但现在有另一种选择。您可以使用go-homedir将 tidle 扩展到用户的 homedir:
myPath := "~/.ssh"
fmt.Printf("path: %s; with expansion: %s", myPath, homedir.Expand(myPath))
Run Code Online (Sandbox Code Playgroud)