Як отримати каталог файлу, який зараз працює?


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

Але це правильний спосіб чи ідіоматичний спосіб у Голангу?


це не відповідь на ваше запитання, але ви можете кешувати шлях до глобального вару (розташування вашого файлу не може бути змінено під час роботи :)), щоб не запускати os.open знову і знову щоразу, коли ваш код запускається
oguzalb

Вам слід пройти 0, не 1, до runtime.Caller().
fiatjaf

4
runtime.Caller(0)дасть вам шлях до вихідного файлу, наприклад $GOPATH/src/packagename/main.go. Інші відповіді в цій темі намагаються повернути шлях до виконуваного файлу (як $GOPATH/bin/packagename).
фіатхаф

Ви припускаєте, що програма працює з файлу ...
Flimzy

Відповіді:


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 замість папки вихідного файлу). Щоб отримати шлях до папки вашого вихідного файлу без використання зовнішньої залежності, використовуйте трохи модифіковану версію коду ОП: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(зверніть увагу runtime.Caller(0)замість runtime.Caller(1))
TanguyP

295

EDIT: Станом на Go 1.8 (вийшов у лютому 2017 року) рекомендований спосіб зробити це за допомогою os.Executable:

func Executable() (string, error)

Виконавчий файл повертає ім'я шляху для виконуваного файлу, який розпочав поточний процес. Немає гарантії, що шлях все ще вказує на правильний виконуваний файл. Якщо для запуску процесу було використано символьне посилання, залежно від операційної системи, результатом може бути символьне посилання або шлях, на який він вказував. Якщо потрібен стабільний результат, шлях / 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)
}

3
Це поточний робочий каталог процесів. У nodejs це еквівалентно process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna

2
Гаразд, я бачу відмінність. З вами після розташування двійкового файлу в файлі файлів (а не поточного робочого каталогу) Я думаю, що runtime.Callerце найближче до вас, щоб потрапити до "ідіоматичного"
Intermernet

3
"Вийшов у лютому 2017 року"? Здається, машина часу була винайдена, і ми маємо членів, які розміщують повідомлення з майбутнього. Приємно знати, що в майбутній версії буде надійний крос-платформний метод, а поки що ми повинні дотримуватися наявних рішень.
ljgww

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. Ці бінарні файли кожного разу поміщаються у тимчасову папку.
Доброслав borybort

2
@DerekDowling спосіб буде спочатку робити a go install, а потім запускати go build -v *.go && ./main. -vБ сказати вам , які файли будуються. Як правило, я виявив, що час різниться між собою go runі go buildє терпимим, якщо я вже бігав go install. Для користувачів Windows в Powerhell команда будеgo build -v {*}.go && ./main.exe
kumarharsh

Оскільки це повернеться $GOPATH/bin/, чому б не використати $GOPATH/bin/?
fiatjaf

10
filepath.Abs("./")

Abs повертає абсолютне зображення шляху. Якщо шлях не є абсолютним, його буде з'єднано з поточним робочим каталогом, щоб перетворити його в абсолютний шлях.

Як зазначено в коментарі, це повертає каталог, який наразі є активним.


8
Це повертає поточний каталог, а не каталог поточного файлу. Наприклад, це було б інакше, якби виконуваний файл викликався з іншого шляху.
Фуджій

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. не / tmp.
Біт

@Bit Використовуючи базовий шаблон налагодження в такому IDE, так, дайте вам це, потім просто натисніть "Змінити конфігурацію" та заповніть "Каталог виводу", тож ви побачите шлях "os.Args [0]" - те, що ви хочете
ϻαϻɾΣɀO -MaMrEzO

5

Якщо ви використовуєте пакет osext від kardianos і вам потрібно тестувати локально, як, наприклад, прокоментував Дерек Даулінг:

Це добре працює, поки ви не хочете використовувати його з go run main.go для локального розвитку. Не впевнений, як краще обійти це, не будуючи виконуваного файлу заздалегідь.

Рішення цього полягає в тому, щоб зробити утиліту gorun.exe замість використання запуску. Утиліта gorun.exe скомпілює проект за допомогою "go build", після чого запустіть його відразу після цього, у звичайному каталозі вашого проекту.

У мене виникла ця проблема з іншими компіляторами, і я виявив, що я роблю ці утиліти, оскільки вони не постачаються разом із компілятором ... Це особливо приховано з такими інструментами, як C, де потрібно компілювати та зв’язувати, а потім запускати його (занадто багато роботи).

Якщо комусь подобається моя ідея про gorun.exe (або ельф), я, швидше за все, незабаром завантажую його в github ..

Вибачте, ця відповідь розуміється як коментар, але я не можу коментувати через те, що ще не маю репутації досить великої.

Крім того, "go run" може бути змінено (якщо у нього вже немає цієї функції), щоб мати такий параметр, як "go run -notemp", щоб не запускати програму у тимчасовому каталозі (або щось подібне). Але я вважаю за краще просто ввести горун або "гор", оскільки він коротший, ніж перекручений параметр. Gorun.exe або gor.exe потрібно буде встановити в тому самому каталозі, що і ваш компілятор go

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


3
Якщо ви хочете, щоб це працювало з "ідіть бігом" та вбудованим виконуваним файлом, тоді просто використовуйте _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)замість цього
Jocelyn

@Jocelyn, ваш коментар настільки чудовий, що ви повинні зробити це повною відповіддю! Це, безумовно, зробило для мене хитрість - в моєму власному налаштуванні я маю локальну копію оточення в macOS, яку я здебільшого використовую для лову синтаксичних помилок (і декількох семантичних); тоді я синхронізую код на сервер розгортання, який працює під Ubuntu Linux, і, звичайно, середовище зовсім інше ... тому є реальна потреба розібратися, куди файлові шляхи належним чином завантажувати шаблони, файли конфігурації, статичні файли тощо ...
Gwyneth Llewelyn

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

4

Іноді цього достатньо, першим аргументом завжди буде шлях до файлу

package main

import (
    "fmt"
    "os"
)


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

    // or
    dir, _ := os.Getwd()
    fmt.Println(dir)
}

0

Відповідь Густаво Німейєра чудова. Але в Windows, час виконання програми здебільшого знаходиться в іншому режимі, як це:

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

Якщо ви використовуєте відносний шлях до файлу, наприклад "/config/api.yaml", це використовуватиме шлях вашого проекту там, де існує ваш код.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.