Expand tilde to home directory

為{幸葍}努か 提交于 2019-12-17 16:43:14

问题


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:


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.




回答2:


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




回答3:


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").




回答4:


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:


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


来源:https://stackoverflow.com/questions/17609732/expand-tilde-to-home-directory

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!