Bagaimana cara mendapatkan direktori file yang sedang berjalan?

239

Dalam nodejs saya menggunakan __dirname . Apa yang setara dengan ini di Golang?

Saya telah mencari di Google dan menemukan artikel ini http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Di mana dia menggunakan kode di bawah ini

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

Tetapi apakah ini cara yang tepat atau idiomatis untuk dilakukan di Golang?

ekanna
sumber
ini bukan jawaban untuk pertanyaan Anda tetapi Anda dapat men-cache jalan ke global var (lokasi file Anda tidak dapat diubah saat menjalankan :)) untuk tidak menjalankan os.open lagi dan lagi setiap kali kode Anda berjalan
oguzalb
Anda harus lulus 0, bukan 1ke runtime.Caller().
fiatjaf
4
runtime.Caller(0)akan memberi Anda path file sumber, seperti $GOPATH/src/packagename/main.go. Jawaban lain di utas ini mencoba mengembalikan jalur yang dapat dieksekusi (seperti $GOPATH/bin/packagename).
fiatjaf
Anda mengasumsikan program berjalan dari file ...
Flimzy
1
Kemungkinan duplikat Go: cari jalan menuju yang dapat dieksekusi
Jocelyn

Jawaban:

221

Ini harus dilakukan:

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)
}
Gustavo Niemeyer
sumber
2
Apakah mungkin ada kesalahan di sini? Jika demikian, apa kesalahannya, hanya karena penasaran?
Jeff Escalante
4
Tidak berfungsi untuk saya play.golang.org/p/c8fe-Zm_bH - os.Args [0] tidak selalu mengandung jalur abs.
zupa
5
Ini benar-benar berfungsi bahkan jika os.Args [0] tidak mengandung path abs. Alasan mengapa hasil bermain ini tidak seperti yang Anda harapkan adalah karena itu ada di dalam kotak pasir.
Gustavo Niemeyer
37
Ini bukan cara yang dapat diandalkan , lihat jawaban tentang menggunakan osext karena ini adalah implementasi yang bekerja dengan semua klien kami di berbagai OS. Saya telah menerapkan kode menggunakan metode ini tetapi tampaknya tidak terlalu dapat diandalkan dan banyak pengguna mengeluhkan bug yang disebabkan oleh metode ini memilih jalur yang salah untuk dieksekusi.
JD D
5
Mendapat hasil yang sama dengan emrah menggunakan Go 1.6 di Windows (mendapat path temp folder daripada folder file sumber). Untuk mendapatkan lintasan folder file sumber Anda tanpa menggunakan dependensi eksternal apa pun, gunakan versi kode OP yang sedikit dimodifikasi: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(perhatikan runtime.Caller(0)gantinya runtime.Caller(1))
TanguyP
295

EDIT: As of Go 1.8 (Dirilis Februari 2017) cara yang disarankan untuk melakukan ini adalah dengan os.Executable:

func Executable() (string, error)

Executable mengembalikan nama path untuk executable yang memulai proses saat ini. Tidak ada jaminan bahwa path masih menunjuk ke executable yang benar. Jika symlink digunakan untuk memulai proses, tergantung pada sistem operasi, hasilnya mungkin symlink atau jalur yang ditunjuk. Jika diperlukan hasil yang stabil, jalur / filepath.EvalSymlinks mungkin membantu.

Untuk mendapatkan direktori executable yang bisa Anda gunakan path/filepath.Dir.

Contoh :

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

JAWABAN TUA:

Anda harus bisa menggunakannya os.Getwd

func Getwd() (pwd string, err error)

Getwd mengembalikan nama jalur yang di-root sesuai dengan direktori saat ini. Jika direktori saat ini dapat dicapai melalui beberapa jalur (karena tautan simbolik), Getwd dapat mengembalikan salah satu dari mereka.

Sebagai contoh:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}
Intermernet
sumber
3
Ini adalah direktori kerja proses saat ini. Dalam nodejs itu setara dengan process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna
2
Ok, saya melihat perbedaannya. Tentang Anda setelah lokasi biner di filesytem (daripada direktori kerja saat ini) saya pikir itu runtime.Calleradalah yang terdekat Anda akan mendapatkan "idiomatik"
Intermernet
3
'Dirilis Februari 2017'? Tampaknya mesin waktu telah ditemukan dan kami memiliki anggota yang memposting dari masa depan. Sangat menyenangkan untuk mengetahui versi masa depan akan memiliki metode lintas platform yang dapat diandalkan, Sementara itu kita harus tetap berpegang pada solusi yang tersedia saat ini.
ljgww
1
@ ljgww Maaf, saya akan membawa Delorean saya dan pulang :-) Saya memperbarui jawaban saya sebelumnya karena saya baru saja melihat fitur yang akan datang dan berpikir saya akan lupa untuk memperbarui jawaban nanti.
Intermernet
1
Diedit dengan path/filepath.Dirkarena path.Dirhanya berfungsi dengan garis miring ke depan (gaya Unix) sebagai pemisah direktori.
Jocelyn
63

Gunakan paket osext

Ini menyediakan fungsi ExecutableFolder()yang mengembalikan jalur absolut ke folder tempat program yang saat ini dijalankan dapat dijalankan (berguna untuk pekerjaan cron). Ini lintas platform.

Dokumentasi online

package main

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

func main() {
    folderPath, err := osext.ExecutableFolder()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(folderPath)
}
Dobrosław Żybort
sumber
13
Ini adalah satu-satunya jawaban yang menghasilkan hasil yang diharapkan untuk saya baik di Windows maupun di Linux.
DannyB
3
Ini berfungsi dengan baik sampai Anda ingin menggunakannya go run main.gountuk pengembangan lokal. Tidak yakin bagaimana cara terbaik untuk menyiasatinya tanpa membangun executable terlebih dahulu setiap kali.
Derek Dowling
1
Maaf saya tidak tahu cara membuatnya bekerja go run. Binari ini diletakkan di folder sementara setiap kali.
Dobrosław Żybort
2
@DerekDowling cara akan menjadi yang pertama melakukan go install, kemudian berjalan go build -v *.go && ./main. Ini -vakan memberi tahu Anda file mana yang sedang dibangun. Secara umum, saya telah menemukan bahwa waktu berbeda antara go rundan go builddapat ditoleransi jika saya sudah menjalankan go install. Untuk pengguna windows di powershell, perintahnya adalahgo build -v {*}.go && ./main.exe
kumarharsh
Karena ini akan kembali $GOPATH/bin/, mengapa tidak digunakan $GOPATH/bin/?
fiatjaf
10
filepath.Abs("./")

Abs mengembalikan representasi absolut jalan. Jika path tidak absolut, itu akan bergabung dengan direktori kerja saat ini untuk mengubahnya menjadi path absolut.

Seperti yang dinyatakan dalam komentar, ini mengembalikan direktori yang saat ini aktif.

Asam 9
sumber
8
Ini mengembalikan direktori saat ini, bukan direktori file saat ini. Sebagai contoh, ini akan berbeda jika executable dipanggil dari jalur yang berbeda.
Fujii
8

jika Anda menggunakan cara ini:

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

Anda akan mendapatkan path / tmp ketika Anda menjalankan program menggunakan beberapa IDE seperti GoLang karena executable akan menyimpan dan menjalankan dari / tmp

Saya pikir cara terbaik untuk mendapatkan Direktori Pekerjaan saat ini atau '.' adalah :

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

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

fungsi os.Getwd () akan mengembalikan direktori kerja saat ini. dan semuanya tanpa menggunakan perpustakaan eksternal apa pun: D

Sedikit
sumber
Ini tidak benar. Ini mengembalikan direktori kerja pengguna yang menjalankan proses dan bukan direktori file. Gunakan filepath.abs.
PodTech.io
1
ini mengembalikan Direktori kerja dari file yang dapat dijalankan yang berjalan. maka jika Anda menggunakan IDE seperti goland dan tidak ada konfigurasi untuk direktori kerja dalam opsi build maka ia akan berjalan dari / tmp, lalu apa gunanya / tmp untuk Anda! ?? tetapi jika Anda menggunakan os.Getwd () itu mengembalikan jalur file executable .exe atau elf. bukan / tmp.
Bit
@Bit Menggunakan template dasar dari debugging dalam IDE seperti itu, ya beri Anda itu, lalu tekan saja 'Edit Konfigurasi' dan isi 'Output Directory', sehingga Anda akan melihat 'os.Args [0]' path adalah apa yang Anda inginkan
ϻαϻɾΣɀО -MaMrEzO
5

Jika Anda menggunakan paket osext oleh kardianos dan Anda perlu menguji secara lokal, seperti Derek Dowling berkomentar:

Ini berfungsi dengan baik sampai Anda ingin menggunakannya dengan go run main.go untuk pengembangan lokal. Tidak yakin bagaimana cara terbaik untuk menyiasatinya tanpa membangun executable terlebih dahulu setiap kali.

Solusi untuk ini adalah membuat utilitas gorun.exe alih-alih menggunakan go run. Utilitas gorun.exe akan mengkompilasi proyek menggunakan "pergi membangun", kemudian jalankan segera setelah itu, di direktori normal proyek Anda.

Saya punya masalah dengan kompiler lain dan menemukan diri saya membuat utilitas ini karena mereka tidak dikirim dengan kompiler ... itu terutama misterius dengan alat-alat seperti C di mana Anda harus mengkompilasi dan menghubungkan dan kemudian menjalankannya (terlalu banyak pekerjaan).

Jika ada yang menyukai ide saya tentang gorun.exe (atau peri), saya akan segera mengunggahnya ke github ..

Maaf, jawaban ini dimaksudkan sebagai komentar, tetapi saya tidak dapat berkomentar karena saya belum memiliki reputasi yang cukup besar.

Atau, "go run" dapat dimodifikasi (jika belum memiliki fitur ini) untuk memiliki parameter seperti "go run -notemp" untuk tidak menjalankan program dalam direktori sementara (atau yang serupa). Tapi saya lebih suka mengetik gorun atau "gor" karena lebih pendek dari parameter yang berbelit-belit. Gorun.exe atau gor.exe perlu diinstal di direktori yang sama dengan kompiler go Anda

Menerapkan gorun.exe (atau gor.exe) akan sepele, karena saya telah melakukannya dengan kompiler lain hanya dalam beberapa baris kode ... (kata-kata terakhir yang terkenal ;-)

Prog lain
sumber
3
Jika Anda ingin keduanya berfungsi dengan "menjalankan" dan membangun yang dapat dieksekusi kemudian gunakan _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)saja
Jocelyn
@Jocelyn, komentar Anda sangat bagus sehingga Anda harus membuatnya menjadi jawaban penuh! Ini tentu melakukan trik bagi saya - pada pengaturan saya sendiri, saya memiliki salinan lokal dari lingkungan di macOS, yang kebanyakan saya gunakan untuk menangkap kesalahan sintaks (dan beberapa yang semantik); kemudian saya menyinkronkan kode ke server penyebaran, yang berjalan di bawah Ubuntu Linux, dan tentu saja lingkungannya sangat berbeda ... jadi ada kebutuhan nyata untuk mencari tahu di mana jalur file untuk memuat template, file konfigurasi, statis dengan benar file, dll ...
Gwyneth Llewelyn
4

Terkadang ini cukup, argumen pertama akan selalu menjadi path file

package main

import (
    "fmt"
    "os"
)


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

    // or
    dir, _ := os.Getwd()
    fmt.Println(dir)
}
Yitzchak Weingarten
sumber
0

Jawaban Gustavo Niemeyer luar biasa. Tetapi di Windows, runtime proc sebagian besar berada di dir lain, seperti ini:

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

Jika Anda menggunakan jalur file relatif, seperti "/config/api.yaml", ini akan menggunakan jalur proyek Anda di mana kode Anda ada.

flypapi
sumber