2013-07-12 5 views
22

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

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

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 в качестве места назначения.

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

ответ

6

Обычно ~ разворачивается оболочкой до вашей программой видит.
Отрегулируйте, как ваша программа получает свои аргументы из командной строки способом, совместимым с механизмом расширения оболочки.

Одна из возможных проблем с использованием 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».

+0

Спасибо, я не знал, что оболочка делает это. – lukad

+0

@jnml, я бы сказал, что первая одинарная цитата в строке формата неуместна и вместо этого должна быть справа перед ''% q' заполнителем для чтения 'fmt.Sprintf (" some-binary '% q' ", someArg)' – kostix

+0

@ kostix: Я так не думаю. Значение, возвращаемое Sprintf в приведенном выше примере, должно быть '' some-binary '/ home/login/foo "''. Не тестировал его, возможно, внешние одиночные кавычки должны быть фактически удалены. Однако они верны в командной строке. – zzzz

52

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 (вероятно, из соображений безопасности), поэтому я не могу дать простой пример).

+0

Не работает допустимые пути, такие как '~/a ~ z'. – zzzz

+0

Действительно? Последний параметр определяет количество экземпляров для замены; он должен остановиться после первого. – joshlf

+0

Да, это работает правильно: http://play.golang.org/p/hvH7u_kwTT – joshlf

10

В общем ~ расширена вашей оболочкой до, которая попадает в вашу программу. Но есть some limitations.

В целом ill-advised to do it manually in Go.

У меня была такая же проблема в моей программе, и я понял, что если я использую формат флага как --flag=~/myfile, он не расширяется. Но если вы запустите --flag ~/myfile, он расширится оболочкой (отсутствует = и имя файла отображается как отдельное «слово»).

3

Если вы используете tilde '~' для использования с 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 на (системного вызова OSX/Linux). В окнах для определения домашней директории пользователей используется системный вызов OpenCurrentProcessToken.

+1

Удивительный! Спасибо за предоставление полного примера (с проверкой ошибок и всем!) Но вам также нужно «импортировать» путь/путь к файлу »или ваш код не будет работать. –

2

Я знаю, что это старый вопрос, но есть другой вариант. Вы можете использовать go-homedir расширить tidle в домашнем каталоге пользователя:

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