Как получить каталог текущего запущенного файла?

239

В nodejs я использую __dirname . Что эквивалентно этому на Голанге?

Я гуглил и обнаружил эту статью http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Где он использует приведенный ниже код

_, filename, _, _ := runtime.Caller(1)
f, err := os.Open(path.Join(path.Dir(filename), "data.csv"))

Но это правильный или идиоматический способ сделать на Голанге?

ekanna
источник
это не ответ на ваш вопрос, но вы можете кэшировать путь к глобальному var (ваше местоположение файла не может быть изменено во время работы :)), чтобы не запускать os.open снова и снова при каждом запуске кода
oguzalb
Вы должны пройти 0, а не 1, чтобы runtime.Caller().
Пятница,
4
runtime.Caller(0)даст вам путь к исходному файлу, как $GOPATH/src/packagename/main.go. Другие ответы в этой теме пытаются вернуть путь к исполняемому файлу (например $GOPATH/bin/packagename).
Пятница,
Вы предполагаете, что программа запускается из файла ...
Flimzy
1
Возможный дубликат Go: найдите путь к исполняемому файлу
Jocelyn

Ответы:

221

Это должно сделать это:

import (
    "fmt"
    "log"
    "os"
    "path/filepath"
)

func main() {
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
    if err != nil {
            log.Fatal(err)
    }
    fmt.Println(dir)
}
Густаво Нимейер
источник
2
Возможно ли здесь быть ошибка? Если так, то что за ошибка, просто из любопытства?
Джефф Эскаланте
4
У меня не работает play.golang.org/p/c8fe-Zm_bH - os.Args [0] не обязательно содержит путь abs.
zupa
5
Это на самом деле работает, даже если os.Args [0] не содержит путь abs. Причина, по которой результат на игровой площадке не тот, который вы ожидали, заключается в том, что он находится внутри песочницы
Густаво Нимейер
37
Это ненадежный способ , смотрите ответ об использовании osext, так как это была реализация, которая работала со всеми нашими клиентами в различных ОС. Я реализовал код, используя этот метод, но он кажется не очень надежным, и многие пользователи жаловались на ошибки, вызванные этим методом, при выборе неправильного пути для исполняемого файла.
JD D
5
Получил тот же результат, что и Emrah, используя Go 1.6 в Windows (вместо папки с исходным файлом был указан путь к папке temp). Чтобы получить путь к папке вашего исходного файла без использования каких-либо внешних зависимостей, используйте слегка измененную версию кода OP: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(обратите внимание на runtime.Caller(0)вместо runtime.Caller(1))
TanguyP
295

РЕДАКТИРОВАТЬ: Начиная с Go 1.8 (выпущен в феврале 2017 года) рекомендуемый способ сделать это с os.Executable:

func Executable() (string, error)

Исполняемый файл возвращает путь к исполняемому файлу, запустившему текущий процесс. Нет гарантии, что путь все еще указывает на правильный исполняемый файл. Если для запуска процесса использовалась символическая ссылка, в зависимости от операционной системы, результатом может быть символическая ссылка или путь, на который она указала. Если требуется стабильный результат, path / filepath.EvalSymlinks может помочь.

Чтобы получить только каталог исполняемого файла, который вы можете использовать path/filepath.Dir.

Пример :

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    ex, err := os.Executable()
    if err != nil {
        panic(err)
    }
    exPath := filepath.Dir(ex)
    fmt.Println(exPath)
}

СТАРЫЙ ОТВЕТ:

Вы должны быть в состоянии использовать os.Getwd

func Getwd() (pwd string, err error)

Getwd возвращает корневое имя пути, соответствующее текущему каталогу. Если текущий каталог доступен по нескольким путям (из-за символических ссылок), Getwd может вернуть любой из них.

Например:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}
Intermernet
источник
3
Это текущий рабочий каталог. В nodejs это эквивалентно process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna
2
Хорошо, я вижу различие. Что касается вас, после размещения двоичного файла в файловой системе (а не в текущем рабочем каталоге), я думаю, что runtime.Callerвы ближе всего к "идиоматическим"
Intermernet
3
«Дата выхода февраля 2017»? Кажется, что машина времени была изобретена, и у нас есть участники, отправляющие из будущего. Приятно знать, что в будущей версии будет надежный кроссплатформенный метод. Тем временем мы должны придерживаться доступных на данный момент решений.
17
1
@ljgww Извините, я возьму свой Delorean и пойду домой :-) Я заранее обновил свой ответ, потому что я только что увидел эту предстоящую функцию и решил, что забуду обновить ответ позже.
Intermernet
1
Отредактировано, path/filepath.Dirпотому что path.Dirработает только с косыми чертами (стиль Unix) в качестве разделителей каталогов.
Джоселин
63

Используйте пакет osext

Он предоставляет функцию, ExecutableFolder()которая возвращает абсолютный путь к папке, в которой находится исполняемый в данный момент исполняемый файл программы (полезно для заданий cron). Это кроссплатформенная.

Интернет документация

package main

import (
    "github.com/kardianos/osext"
    "fmt"
    "log"
)

func main() {
    folderPath, err := osext.ExecutableFolder()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(folderPath)
}
Доброслав Жиборт
источник
13
Это единственный ответ, который принес мне ожидаемые результаты как в Windows, так и в Linux.
DannyB
3
Это прекрасно работает, пока вы не захотите использовать его go run main.goдля локальной разработки. Не уверен, как лучше обойти это, не создавая исполняемый файл заранее каждый раз.
Дерек Даулинг
1
Извините, я не знаю, как заставить это работать go run. Эти двоичные файлы помещаются во временную папку каждый раз.
Доброслав Жиборт
2
@DerekDowling способ будет сначала сделать, а go installзатем запустить go build -v *.go && ./main. Он -vскажет вам, какие файлы создаются. Вообще, я обнаружил, что время отличается go runи go buildтерпимо, если я уже бегал go install. Для пользователей Windows на PowerShell эта команда будетgo build -v {*}.go && ./main.exe
kumarharsh
Так как это вернется $GOPATH/bin/, почему бы не использовать $GOPATH/bin/?
Пятница,
10
filepath.Abs("./")

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

Как указано в комментарии, возвращается каталог, который в данный момент активен.

Acidic9
источник
8
Это возвращает текущий каталог, а не каталог текущего файла. Например, это было бы иначе, если бы исполняемый файл вызывался с другого пути.
Fujii
8

если вы используете этот способ:

dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
    log.Fatal(err)
}
fmt.Println(dir)

вы получите путь / tmp при запуске программы с использованием некоторой IDE, такой как GoLang, потому что исполняемый файл будет сохраняться и запускаться из / tmp

Я думаю, что лучший способ получить текущий рабочий каталог или "." является :

import(
  "os" 
  "fmt"
  "log"
)

func main() {
  dir, err := os.Getwd()
    if err != nil {
        log.Fatal(err)
    }
  fmt.Println(dir)
}

os.Getwd () функция возвращает текущий рабочий каталог. и все это без использования какой-либо внешней библиотеки: D

Немного
источник
Это не правильно. Это возвращает рабочий каталог пользователя, выполняющего процесс, а не каталог файла. Используйте filepath.abs.
PodTech.io
1
он возвращает рабочий каталог запущенного исполняемого файла. затем, если вы используете IDE, такую ​​как goland, и в опциях сборки нет конфигурации для рабочего каталога, тогда он будет запускаться из / tmp, тогда какое использование / tmp имеет для вас! ??, но если вы используете os.Getwd (), то это возвращает путь к исполняемому файлу .exe или elf не / тмп.
Бит
@Bit Используя базовый шаблон отладки в такой IDE, да, дайте вам это, затем просто нажмите «Редактировать конфигурацию» и заполните «Выходной каталог», чтобы вы увидели, что путь os.Args [0] - это то, что вы хотите
ϻαϻɾΣɀО -MaMrEzO
5

Если вы используете пакет osext от kardianos и вам нужно тестировать локально, как прокомментировал Дерек Даулинг:

Это прекрасно работает, пока вы не захотите использовать его с go run main.go для локальной разработки. Не уверен, как лучше обойти это, не создавая исполняемый файл заранее каждый раз.

Решением этой проблемы является создание утилиты gorun.exe вместо использования go run. Утилита gorun.exe скомпилирует проект, используя «go build», а затем сразу же запустит его в обычном каталоге вашего проекта.

У меня была эта проблема с другими компиляторами, и я обнаружил, что создаю эти утилиты, так как они не поставляются вместе с компилятором ... это особенно непонятно с такими инструментами, как C, где вам нужно скомпилировать и связать, а затем запустить его (слишком много работы).

Если кому-то нравится моя идея gorun.exe (или elf), я, скорее всего, скоро выложу его на github.

Извините, этот ответ подразумевается как комментарий, но я не могу комментировать, потому что у меня пока недостаточно высокая репутация.

В качестве альтернативы, «go run» можно изменить (если он еще не имеет этой функции), чтобы иметь такой параметр, как «go run -notemp», чтобы не запускать программу во временном каталоге (или что-то подобное). Но я бы предпочел просто набрать gorun или «gor», так как он короче запутанного параметра. Gorun.exe или gor.exe должны быть установлены в том же каталоге, что и ваш компилятор go

Реализация gorun.exe (или gor.exe) была бы тривиальной, как я сделал это с другими компиляторами всего в нескольких строках кода ... (известные последние слова ;-)

Еще одна прога
источник
3
Если вы хотите, чтобы он работал как с «go run», так и со встроенным исполняемым файлом, просто используйте _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)вместо этого
Jocelyn
@Jocelyn, ваш комментарий настолько хорош, что вы должны превратить его в полный ответ! Это, безусловно, помогло мне - на моей собственной установке у меня есть локальная копия среды в macOS, которую я в основном использую для обнаружения синтаксических ошибок (и нескольких семантических); затем я синхронизирую код с сервером развертывания, который работает под Ubuntu Linux, и, конечно, среда совершенно иная ... поэтому существует реальная необходимость выяснить, где пути к файлам для правильной загрузки шаблонов, файлов конфигурации, статических данных. файлы и т.д ...
Гвинет Ллевелин
4

os.Executable: https://tip.golang.org/pkg/os/#Executable

filepath.EvalSymlinks: https://golang.org/pkg/path/filepath/#EvalSymlinks

Полная демонстрация:

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    var dirAbsPath string
    ex, err := os.Executable()
    if err == nil {
        dirAbsPath = filepath.Dir(ex)
        fmt.Println(dirAbsPath)
        return
    }

    exReal, err := filepath.EvalSymlinks(ex)
    if err != nil {
        panic(err)
    }
    dirAbsPath = filepath.Dir(exReal)
    fmt.Println(dirAbsPath)
}
vikyd
источник
4

Иногда этого достаточно, первым аргументом всегда будет путь к файлу

package main

import (
    "fmt"
    "os"
)


func main() {
    fmt.Println(os.Args[0])

    // or
    dir, _ := os.Getwd()
    fmt.Println(dir)
}
Ицхак Вайнгартен
источник
0

Ответ Густаво Нимейера великолепен. Но в Windows, время выполнения proc в основном в другом каталоге, например:

"C:\Users\XXX\AppData\Local\Temp"

Если вы используете относительный путь к файлу, как "/config/api.yaml" , это будет использовать путь вашего проекта, где ваш код существует.

flypapi
источник