Подтвердить что ты не робот

Развернуть тильду в домашний каталог

У меня есть программа, которая принимает папку назначения, в которой будут созданы файлы. Моя программа должна иметь возможность обрабатывать абсолютные пути, а также относительные пути. Моя проблема в том, что я не знаю, как развернуть ~ в домашний каталог.

Моя функция для расширения адресата выглядит так. Если заданный путь является абсолютным, он ничего не делает, иначе он соединяет относительный путь с текущим рабочим каталогом.

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 в качестве адресата.

Как я могу решить это на кросс-платформенном пути?

4b9b3361

Ответ 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))