Развернуть тильду в домашний каталог
У меня есть программа, которая принимает папку назначения, в которой будут созданы файлы. Моя программа должна иметь возможность обрабатывать абсолютные пути, а также относительные пути. Моя проблема в том, что я не знаю, как развернуть ~
в домашний каталог.
Моя функция для расширения адресата выглядит так. Если заданный путь является абсолютным, он ничего не делает, иначе он соединяет относительный путь с текущим рабочим каталогом.
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
в качестве адресата.
Как я могу решить это на кросс-платформенном пути?
Ответы
Ответ 1
Обычно ~
расширяется оболочкой, прежде чем ваша программа увидит ее.
Отрегулируйте, как ваша программа получает свои аргументы из командной строки способом, совместимым с механизмом расширения оболочки.
Одна из возможных проблем заключается в использовании exec.Command следующим образом:
cmd := exec.Command("some-binary", someArg) // say 'someArg' is "~/foo"
который не будет расширяться. Вы можете, например, использовать вместо этого:
cmd := exec.Command("sh", "-c", fmt.Sprintf("'some-binary %q'", someArg))
который получит стандартное расширение ~
из оболочки.
EDIT: исправлен пример 'sh -c'.
Ответ 2
Go предоставляет пакет os/user, который позволяет получить текущего пользователя и любого пользователя в своем домашнем каталоге:
usr, _ := user.Current()
dir := usr.HomeDir
Затем используйте path/filepath, чтобы объединить обе строки в допустимый путь:
// Check in case of paths like "/something/~/something/"
if path[:2] == "~/" {
path = filepath.Join(dir, path[2:])
}
(Обратите внимание, что user.Current() не реализован на игровой площадке go (вероятно, из соображений безопасности), поэтому я не могу дать легко запускаемый пример).
Ответ 3
В общем случае ~
расширяется вашей оболочкой до того, как она попадет в вашу программу. Но есть некоторые ограничения.
В общем случае не рекомендуется делать это вручную в Go.
У меня была такая же проблема в моей программе, и я понял, что если я использую формат флага как --flag=~/myfile
, он не будет расширяться. Но если вы запустите --flag ~/myfile
, он будет расширен оболочкой (отсутствует =
и имя файла будет отображаться как отдельное "слово" ).
Ответ 4
Если вы расширяете тильду '~' для использования с exec.Command()
, вы должны использовать локальную оболочку пользователей для расширения.
// '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
с помощью getpwuid_r
syscall on (osx/linux). В окнах он использует syscall OpenCurrentProcessToken
для определения домашней директории пользователей.
Ответ 5
Я знаю, что это старый вопрос, но теперь есть другой вариант. Вы можете использовать go-homedir, чтобы развернуть ученик для пользователя homedir:
myPath := "~/.ssh"
fmt.Printf("path: %s; with expansion: %s", myPath, homedir.Expand(myPath))