Come ottenere la directory del file attualmente in esecuzione?


239

In nodejs uso __dirname . Qual è l'equivalente di questo in Golang?

Ho cercato su Google e ho scoperto questo articolo http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Dove usa sotto il codice

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

Ma è il modo giusto o il modo idiomatico di fare a Golang?


questa non è una risposta alla tua domanda ma puoi memorizzare nella cache il percorso di una var globale (la posizione del tuo file non può essere modificata durante l'esecuzione :)) per non eseguire os.open ancora e ancora ogni volta che il tuo codice viene eseguito
oguzalb

Dovresti passare 0, non 1a runtime.Caller().
fiatjaf,

4
runtime.Caller(0)ti darà il percorso del file sorgente, come $GOPATH/src/packagename/main.go. Le altre risposte in questo thread stanno provando a restituire il percorso dell'eseguibile (come $GOPATH/bin/packagename).
fiatjaf,


Risposte:


221

Questo dovrebbe farlo:

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
È possibile che ci sia un errore qui? In tal caso, quale sarebbe l'errore, solo per curiosità?
Jeff Escalante,

4
Non funziona per me play.golang.org/p/c8fe-Zm_bH - os.Args [0] non contiene necessariamente il percorso abs.
Zupa,

5
Funziona effettivamente anche se os.Args [0] non contiene il percorso abs. Il motivo per cui il risultato del parco giochi non è quello che ti aspettavi è perché è all'interno di una sandbox.
Gustavo Niemeyer,

37
Questo non è un modo affidabile , vedi la risposta sull'uso di osext poiché questa era un'implementazione che funzionava con tutti i nostri client su vari sistemi operativi. Avevo implementato il codice utilizzando questo metodo, ma sembra non essere molto affidabile e molti utenti si sono lamentati dei bug causati da questo metodo scegliendo il percorso sbagliato per l'eseguibile.
JD D,

5
Ha ottenuto lo stesso risultato di emrah usando Go 1.6 su Windows (ottenuto il percorso della cartella temporanea invece della cartella del file di origine). Per ottenere il percorso della cartella del tuo file sorgente senza usare alcuna dipendenza esterna, usa una versione leggermente modificata del codice OP: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(nota runtime.Caller(0)invece di runtime.Caller(1))
TanguyP

295

EDIT: A partire da Go 1.8 (rilasciato a febbraio 2017) il modo raccomandato per farlo è con os.Executable:

func Executable() (string, error)

Il file eseguibile restituisce il nome del percorso del file eseguibile che ha avviato il processo corrente. Non vi è alcuna garanzia che il percorso stia ancora puntando all'eseguibile corretto. Se un collegamento simbolico è stato utilizzato per avviare il processo, a seconda del sistema operativo, il risultato potrebbe essere il collegamento simbolico o il percorso a cui puntava. Se è necessario un risultato stabile, path / filepath.EvalSymlinks potrebbe aiutare.

Per ottenere solo la directory dell'eseguibile è possibile utilizzare path/filepath.Dir.

Esempio :

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

VECCHIA RISPOSTA:

Dovresti essere in grado di usare os.Getwd

func Getwd() (pwd string, err error)

Getwd restituisce un nome di percorso rooted corrispondente alla directory corrente. Se la directory corrente è raggiungibile tramite più percorsi (a causa di collegamenti simbolici), Getwd può restituire uno di essi.

Per esempio:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}

3
Questa è la directory di lavoro del processo corrente. In nodejs è equivalente a process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna,

2
Ok, vedo la distinzione. Stai cercando la posizione del binario nel filesystem (piuttosto che l'attuale directory di lavoro) Penso che runtime.Callersia il più vicino che otterrai a "idiomatico"
Intermernet,

3
"Rilasciato febbraio 2017"? Sembra che la macchina del tempo sia stata inventata e abbiamo membri che inviano dal futuro. È bello sapere che una versione futura avrà un metodo multipiattaforma affidabile, nel frattempo dobbiamo attenerci alle soluzioni attualmente disponibili.
ljgww,

1
@ljgww Mi dispiace, prendo il mio Delorean e torno a casa :-) Ho aggiornato la mia risposta in anticipo perché avevo appena visto quella funzione imminente e ho pensato che avrei dimenticato di aggiornare la risposta in seguito.
Intermernet,

1
Modificato con path/filepath.Dirperché path.Dirfunziona solo con barre (stile Unix) come separatori di directory.
Jocelyn,

63

Usa il pacchetto osext

Fornisce una funzione ExecutableFolder()che restituisce un percorso assoluto alla cartella in cui risiede l'eseguibile del programma attualmente in esecuzione (utile per i lavori cron). È multipiattaforma.

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

13
Questa è l'unica risposta che ha prodotto i risultati attesi per me sia su Windows che su Linux.
DannyB,

3
Funziona bene fino a quando non si desidera utilizzarlo go run main.goper lo sviluppo locale. Non sono sicuro del modo migliore per aggirare il problema senza creare un eseguibile in anticipo ogni volta.
Derek Dowling

1
Mi dispiace non so come farlo funzionare go run. Questi file binari vengono messi ogni volta in una cartella temporanea.
Dobrosław Żybort,

2
@DerekDowling un modo sarebbe prima fare un go install, poi correre go build -v *.go && ./main. Il -vdirei che i file sono in costruzione. In generale, ho scoperto che il tempo è diverso tra go runed go buildè tollerabile se ho già corso go install. Per gli utenti di Windows su PowerShell, il comando saràgo build -v {*}.go && ./main.exe
kumarharsh,

Dal momento che questo tornerà $GOPATH/bin/, perché non usarlo $GOPATH/bin/?
fiatjaf,

10
filepath.Abs("./")

Abs restituisce una rappresentazione assoluta del percorso. Se il percorso non è assoluto, verrà unito alla directory di lavoro corrente per trasformarlo in un percorso assoluto.

Come indicato nel commento, questo restituisce la directory che è attualmente attiva.


8
Ciò restituisce la directory corrente, non la directory del file corrente. Ad esempio, ciò sarebbe diverso se l'eseguibile fosse chiamato da un percorso diverso.
Fujii,

8

se usi in questo modo:

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

otterrai il percorso / tmp quando stai eseguendo un programma usando alcuni IDE come GoLang perché l'eseguibile salverà ed eseguirà da / tmp

penso che il modo migliore per ottenere l'attuale Directory di lavoro o '.' è:

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

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

la funzione os.Getwd () restituirà la directory di lavoro corrente. e tutto senza l'utilizzo di alcuna libreria esterna: D


Questo non è corretto Ciò restituisce la directory di lavoro dell'utente che esegue il processo e non la directory del file. Usa filepath.abs.
PodTech.io

1
restituisce la directory di lavoro del file eseguibile in esecuzione. quindi se stai usando un IDE come goland e non c'è alcuna configurazione per la directory di lavoro nelle opzioni di compilazione, allora verrà eseguito da / tmp, quindi quale uso / tmp ha per te! ?? ma se usi os.Getwd () restituisce il percorso del file eseguibile .exe o elf. non / tmp.
Bit

@Bit Usando il modello base di debug in un tale IDE, sì, dacci questo, quindi premi 'Modifica configurazione' e riempi 'Directory di output', così vedrai il percorso 'os.Args [0]' è quello che vuoi
ϻαϻɾΣɀО -MaMrEzO

5

Se usi il pacchetto osext di kardianos e devi testare localmente, come ha commentato Derek Dowling:

Funziona bene fino a quando non desideri utilizzarlo con go run main.go per lo sviluppo locale. Non sono sicuro del modo migliore per aggirare il problema senza creare un eseguibile in anticipo ogni volta.

La soluzione è creare un'utilità gorun.exe invece di utilizzare go run. L'utilità gorun.exe compilerà il progetto usando "go build", quindi lo eseguirà subito, nella normale directory del progetto.

Ho avuto questo problema con altri compilatori e mi sono ritrovato a creare queste utilità poiché non sono fornite con il compilatore ... è particolarmente arcano con strumenti come C in cui devi compilare e collegare e quindi eseguirlo (troppo lavoro).

Se a qualcuno piace la mia idea di gorun.exe (o elfo) probabilmente lo caricherò presto su github ..

Siamo spiacenti, questa risposta si intende come un commento, ma non posso commentare perché non ho ancora una reputazione abbastanza grande.

In alternativa, "go run" potrebbe essere modificato (se non ha già questa funzione) per avere un parametro come "go run -notemp" per non eseguire il programma in una directory temporanea (o qualcosa di simile). Preferirei semplicemente digitare gorun o "gor" in quanto è più breve di un parametro contorto. Gorun.exe o gor.exe dovrebbero essere installati nella stessa directory del compilatore go

L'implementazione di gorun.exe (o gor.exe) sarebbe banale, come ho fatto con altri compilatori in poche righe di codice ... (ultime parole famose ;-)


3
Se vuoi che funzioni sia con "go run" che con eseguibile compilato, usa semplicemente _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)invece
Jocelyn

@Jocelyn, il tuo commento è così grande che dovresti trasformarlo in una risposta completa! Questo certamente ha fatto il trucco per me - sulla mia configurazione, ho una copia locale dell'ambiente in macOS, che uso principalmente per catturare errori di sintassi (e alcuni semantici); quindi sincronizzo il codice sul server di distribuzione, che gira su Ubuntu Linux, e ovviamente l'ambiente è completamente diverso ... quindi c'è una reale necessità di capire dove devono essere caricati correttamente i percorsi dei file, i file di configurazione, statici file, ecc ...
Gwyneth Llewelyn il


4

A volte questo è sufficiente, il primo argomento sarà sempre il percorso del file

package main

import (
    "fmt"
    "os"
)


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

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

0

La risposta di Gustavo Niemeyer è fantastica. Ma in Windows, proc di runtime è principalmente in un'altra directory, in questo modo:

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

Se si utilizza il percorso del file relativo, ad esempio "/config/api.yaml", questo utilizzerà il percorso del progetto in cui esiste il codice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.