展开波浪线到主目录

huangapple go评论90阅读模式
英文:

Expand tilde to home directory

问题

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

我的扩展目标的函数如下所示。如果给定的路径是绝对路径,则不执行任何操作,否则将相对路径与当前工作目录连接起来。

import "path"
import "os"

// var destination *String 是用户输入

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

由于path.Join不会扩展~,所以如果用户将~/Downloads作为目标传递,它将无法工作。

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

英文:

I have a program that accepts a destination folder where files will be created. My program should be able to handle absolute paths as well as relative paths. My problem is that I don't know how to expand ~ to the home directory.

My function to expand the destination looks like this. If the path given is absolute it does nothing otherwise it joins the relative path with the current working directory.

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

Since path.Join doesn't expand ~ it doesn't work if the user passes something like ~/Downloads as the destination.

How should I solve this in a cross platform way?

答案1

得分: 106

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

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

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

if path == "~" {
// 对于“~”,它不会被“else if”捕获
path = dir
} else if strings.HasPrefix(path, "~/") {
// 使用strings.HasPrefix,以便我们不会匹配像“/something/~/something/”这样的路径
path = filepath.Join(dir, path[2:])
}

(请注意,user.Current()在go playground中没有实现(可能是出于安全原因),因此我无法提供一个容易运行的示例)。

英文:

Go provides the package os/user, which allows you to get the current user, and for any user, their home directory:

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

Then, use path/filepath to combine both strings to a valid path:

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

(Note that user.Current() is not implemented in the go playground (likely for security reasons), so I can't give an easily runnable example).

答案2

得分: 23

一般来说,~ 在你的程序之前会被你的shell展开。但是有一些限制

一般来说,在Go中手动展开是不被推荐的

我在我的一个程序中遇到了同样的问题,我所理解的是,如果我使用--flag=~/myfile这样的标志格式,它不会被展开。但是如果你运行--flag ~/myfile,它会被shell展开(=符号缺失,文件名会作为一个单独的“单词”出现)。

英文:

In general the ~ is expanded by your shell before it gets to your program. But there are some limitations.

In general is ill-advised to do it manually in Go.

I had the same problem in a program of mine and what I have understood is that if I use the flag format as --flag=~/myfile, it is not expanded. But if you run --flag ~/myfile it is expanded by the shell (the = is missing and the filename appears as a separate "word").

答案3

得分: 8

通常情况下,~ 在你的程序看到之前会被shell展开。调整你的程序从命令行获取参数的方式,以与shell展开机制兼容。

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

cmd := exec.Command("some-binary", someArg) // 假设someArg是"~/foo"

这样不会被展开。你可以使用以下方式代替:

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

这样将从shell中获得标准的~展开。

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

英文:

Normally, the ~ is expanded by the shell before your program sees it.
Adjust how your program acquires its arguments from the command line in a way compatible with the shell expansion mechanism.

One of the possible problems is using exec.Command like this:

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

which will not get expanded. You can, for example use instead:

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

which will get the standard ~ expansion from the shell.

EDIT: fixed the 'sh -c' example.

答案4

得分: 8

如果您正在使用exec.Command()来扩展波浪线'~',您应该使用用户的本地shell进行扩展。

// 'sh','bash'和'zsh'都支持'-c'参数
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)上通过getpwuid_r系统调用读取/etc/passwd文件来确定主目录。在Windows上,它使用OpenCurrentProcessToken系统调用来确定用户的主目录。

英文:

If you are expanding tilde '~' for use with exec.Command() you should use the users local shell for expansion.

// '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)
}

However; when loading application config files such as ~./myrc this solution is not acceptable. The following has worked well for me across multiple platforms

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
}

NOTE: usr.HomeDir does not respect $HOME instead determines the home directory by reading the /etc/passwd file via the getpwuid_r syscall on (osx/linux). On windows it uses the OpenCurrentProcessToken syscall to determine the users home directory.

答案5

得分: 7

我知道这是一个旧问题,但现在有另一个选项。您可以使用go-homedir将波浪号扩展到用户的主目录:

myPath := "~/.ssh"
fmt.Printf("路径:%s;扩展后的路径:%s", myPath, homedir.Expand(myPath))
英文:

I know this is an old question but there is another option now. You can use go-homedir to expand the tidle to the user's homedir:

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

答案6

得分: 0

这适用于 go >= 1.12:

if strings.HasPrefix(path, "~/") {
    home, _ := os.UserHomeDir()
    path = filepath.Join(home, path[2:])	
}
英文:

This works on go >= 1.12:

if strings.HasPrefix(path, "~/") {
    home, _ := os.UserHomeDir()
    path = filepath.Join(home, path[2:])	
}

huangapple
  • 本文由 发表于 2013年7月12日 15:30:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/17609732.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定