Wie erhalte ich das Verzeichnis der aktuell ausgeführten Datei?

239

In nodejs verwende ich __dirname . Was ist das Äquivalent dazu in Golang?

Ich habe gegoogelt und diesen Artikel herausgefunden http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Wo er unten Code verwendet

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

Aber ist es der richtige oder idiomatische Weg in Golang?

ekanna
quelle
Dies ist keine Antwort auf Ihre Frage, aber Sie können den Pfad zu einer globalen Variablen zwischenspeichern (Ihr Dateispeicherort kann während der Ausführung nicht geändert werden :)), um os.open nicht jedes Mal auszuführen, wenn Ihr Code ausgeführt wird
oguzalb
Sie sollten 0nicht 1an übergeben runtime.Caller().
Fiatjaf
4
runtime.Caller(0)gibt Ihnen den Pfad der Quelldatei, wie $GOPATH/src/packagename/main.go. Die anderen Antworten in diesem Thread versuchen, den Pfad der ausführbaren Datei (wie $GOPATH/bin/packagename) zurückzugeben.
Fiatjaf
Sie nehmen an, dass das Programm von einer Datei ausgeführt wird ...
Flimzy
1
Mögliches Duplikat von Go: Finden Sie den Pfad zur ausführbaren Datei
Jocelyn

Antworten:

221

Dies sollte es tun:

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
quelle
2
Kann es hier zu einem Fehler kommen? Wenn ja, was wäre der Fehler, nur aus Neugier?
Jeff Escalante
4
Funktioniert bei mir nicht play.golang.org/p/c8fe-Zm_bH - os.Args [0] enthält nicht unbedingt den abs-Pfad.
zupa
5
Es funktioniert auch dann, wenn os.Args [0] den abs-Pfad nicht enthält. Der Grund, warum das Spielplatzergebnis nicht Ihren Erwartungen entspricht, liegt darin, dass es sich in einem Sandkasten befindet.
Gustavo Niemeyer
37
Dies ist kein zuverlässiger Weg , siehe die Antwort zur Verwendung von osext, da dies eine Implementierung war, die mit allen unseren Kunden auf verschiedenen Betriebssystemen funktioniert hat. Ich hatte Code mit dieser Methode implementiert, aber er scheint nicht sehr zuverlässig zu sein, und viele Benutzer haben sich über Fehler beschwert, die durch diese Methode verursacht wurden, indem sie den falschen Pfad für die ausführbare Datei gewählt haben.
JD D
5
Erhielt das gleiche Ergebnis wie Emrah mit Go 1.6 unter Windows (Pfad des temporären Ordners anstelle des Quelldateiordners). Um den Pfad des Ordners Ihrer Quelldatei ohne externe Abhängigkeit abzurufen, verwenden Sie eine leicht modifizierte Version des OP-Codes: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(beachten Sie runtime.Caller(0)stattdessen runtime.Caller(1))
TanguyP
295

BEARBEITEN: Ab Go 1.8 (veröffentlicht im Februar 2017) wird Folgendes empfohlen os.Executable:

func Executable() (string, error)

Executable gibt den Pfadnamen für die ausführbare Datei zurück, die den aktuellen Prozess gestartet hat. Es gibt keine Garantie dafür, dass der Pfad weiterhin auf die richtige ausführbare Datei verweist. Wenn zum Starten des Prozesses ein Symlink verwendet wurde, kann das Ergebnis je nach Betriebssystem der Symlink oder der Pfad sein, auf den er verweist. Wenn ein stabiles Ergebnis benötigt wird, kann path / filepath.EvalSymlinks hilfreich sein.

Um nur das Verzeichnis der ausführbaren Datei zu erhalten, können Sie verwenden path/filepath.Dir.

Beispiel :

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

ALTE ANTWORT:

Sie sollten in der Lage sein zu verwenden os.Getwd

func Getwd() (pwd string, err error)

Getwd gibt einen gerooteten Pfadnamen zurück, der dem aktuellen Verzeichnis entspricht. Wenn das aktuelle Verzeichnis über mehrere Pfade erreichbar ist (aufgrund symbolischer Links), kann Getwd einen dieser Pfade zurückgeben.

Beispielsweise:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}
Internet
quelle
3
Dies ist das aktuelle Prozessarbeitsverzeichnis. In nodejs ist es gleichbedeutend mit process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna
2
Ok, ich sehe den Unterschied. Wenn Sie nach dem Speicherort der Binärdatei im Dateisystem suchen (und nicht nach dem aktuellen Arbeitsverzeichnis), runtime.Callerist dies meiner Meinung nach der "idiomatische"
Intermernet
3
'Veröffentlicht im Februar 2017'? Es scheint, dass die Zeitmaschine erfunden wurde und wir Mitglieder haben, die aus der Zukunft posten. Es ist schön zu wissen, dass eine zukünftige Version eine zuverlässige plattformübergreifende Methode haben wird. In der Zwischenzeit müssen wir uns an die derzeit verfügbaren Lösungen halten.
ljgww
1
@ljgww Entschuldigung, ich nehme meinen Delorean und gehe nach Hause :-) Ich habe meine Antwort im Voraus aktualisiert, da ich diese bevorstehende Funktion gerade erst gesehen hatte und dachte, ich würde vergessen, die Antwort später zu aktualisieren.
Internet
1
Bearbeitet mit, path/filepath.Dirweil path.Dirnur mit Schrägstrichen (Unix-Stil) als Verzeichnistrennzeichen funktioniert.
Jocelyn
63

Verwenden Sie das Paket osext

Es bietet eine Funktion ExecutableFolder(), die einen absoluten Pfad zu dem Ordner zurückgibt, in dem sich die aktuell ausgeführte ausführbare Programmdatei befindet (nützlich für Cron-Jobs). Es ist plattformübergreifend.

Online-Dokumentation

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
quelle
13
Dies ist die einzige Antwort, die sowohl unter Windows als auch unter Linux die erwarteten Ergebnisse für mich erbracht hat.
DannyB
3
Dies funktioniert einwandfrei, bis Sie es go run main.gofür die lokale Entwicklung verwenden möchten. Ich bin mir nicht sicher, wie ich das am besten umgehen kann, ohne vorher jedes Mal eine ausführbare Datei zu erstellen.
Derek Dowling
1
Entschuldigung, ich weiß nicht, wie ich damit arbeiten soll go run. Diese Binärdateien werden jedes Mal in einem temporären Ordner abgelegt.
Dobrosław Żybort
2
@DerekDowling ein Weg wäre zuerst ein go install, dann laufen go build -v *.go && ./main. Das -vwürde Ihnen sagen, welche Dateien erstellt werden. Im Allgemeinen habe ich festgestellt, dass die Zeit zwischen go runund go builderträglich ist, wenn ich bereits gelaufen bin go install. Für Windows-Benutzer auf Powershell wird der Befehl seingo build -v {*}.go && ./main.exe
Kumarharsh
$GOPATH/bin/Warum nicht verwenden, da dies zurückkehren wird $GOPATH/bin/?
Fiatjaf
10
filepath.Abs("./")

Abs gibt eine absolute Darstellung des Pfades zurück. Wenn der Pfad nicht absolut ist, wird er mit dem aktuellen Arbeitsverzeichnis verknüpft, um ihn in einen absoluten Pfad umzuwandeln.

Wie im Kommentar angegeben, gibt dies das aktuell aktive Verzeichnis zurück.

Acidic9
quelle
8
Dies gibt das aktuelle Verzeichnis zurück, nicht das Verzeichnis der aktuellen Datei. Dies wäre beispielsweise anders, wenn die ausführbare Datei von einem anderen Pfad aus aufgerufen würde.
Fujii
8

Wenn Sie diesen Weg verwenden:

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

Sie erhalten den Pfad / tmp, wenn Sie ein Programm mit einer IDE wie GoLang ausführen, da die ausführbare Datei / tmp speichert und ausführt

Ich denke, der beste Weg, um das aktuelle Arbeitsverzeichnis oder '.' ist:

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

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

Die Funktion os.Getwd () gibt das aktuelle Arbeitsverzeichnis zurück. und alles ohne Verwendung einer externen Bibliothek: D.

Bisschen
quelle
Das ist nicht richtig. Dies gibt das Arbeitsverzeichnis des Benutzers zurück, der den Prozess ausführt, und nicht das Verzeichnis der Datei. Verwenden Sie filepath.abs.
PodTech.io
1
Es gibt das Arbeitsverzeichnis der laufenden ausführbaren Datei zurück. Wenn Sie dann eine IDE wie goland verwenden und es keine Konfiguration für das Arbeitsverzeichnis in den Build-Optionen gibt, wird sie von / tmp ausgeführt. Welche Verwendung hat / tmp für Sie? ?? Aber wenn Sie os.Getwd () verwenden Gibt den ausführbaren Dateipfad .exe oder elf zurück. nicht / tmp.
Bit
@Bit Wenn Sie die Basisvorlage für das Debuggen in einer solchen IDE verwenden, geben Sie diese an. Klicken Sie dann einfach auf "Konfiguration bearbeiten" und füllen Sie "Ausgabeverzeichnis" aus, damit der Pfad "os.Args [0]"
angezeigt wird
5

Wenn Sie das Paket osext von kardianos verwenden und lokal testen müssen, wie Derek Dowling kommentierte:

Dies funktioniert einwandfrei, bis Sie es mit go run main.go für die lokale Entwicklung verwenden möchten. Ich bin mir nicht sicher, wie ich das am besten umgehen kann, ohne vorher jedes Mal eine ausführbare Datei zu erstellen.

Die Lösung hierfür besteht darin, ein Dienstprogramm gorun.exe zu erstellen, anstatt go run zu verwenden. Das Dienstprogramm gorun.exe kompiliert das Projekt mit "go build" und führt es direkt danach im normalen Verzeichnis Ihres Projekts aus.

Ich hatte dieses Problem mit anderen Compilern und stellte fest, dass ich diese Dienstprogramme erstellt habe, da sie nicht mit dem Compiler geliefert werden. Dies ist besonders geheimnisvoll bei Tools wie C, bei denen Sie sie kompilieren, verknüpfen und dann ausführen müssen (zu viel Arbeit).

Wenn jemand meine Idee von gorun.exe (oder elf) mag, werde ich sie wahrscheinlich bald auf github hochladen.

Entschuldigung, diese Antwort ist als Kommentar gedacht, aber ich kann keinen Kommentar abgeben, da ich noch keinen Ruf habe, der groß genug ist.

Alternativ könnte "go run" so geändert werden (falls diese Funktion noch nicht vorhanden ist), dass ein Parameter wie "go run -notemp" vorhanden ist, um das Programm nicht in einem temporären Verzeichnis (oder ähnlichem) auszuführen. Aber ich würde es vorziehen, nur gorun oder "gor" einzugeben, da es kürzer als ein verschlungener Parameter ist. Gorun.exe oder gor.exe müssten im selben Verzeichnis wie Ihr go-Compiler installiert werden

Die Implementierung von gorun.exe (oder gor.exe) wäre trivial, da ich es mit anderen Compilern in nur wenigen Codezeilen gemacht habe ... (berühmte letzte Wörter ;-)

Ein weiterer Prog
quelle
3
Wenn Sie möchten, dass es sowohl mit "go run" als auch mit einer erstellten ausführbaren Datei funktioniert, verwenden Sie _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)stattdessen einfach
Jocelyn
@ Jocelyn, dein Kommentar ist so großartig, dass du daraus eine vollständige Antwort machen solltest! Dies hat sicherlich den Trick für mich getan - in meinem eigenen Setup habe ich eine lokale Kopie der Umgebung in macOS, mit der ich hauptsächlich Syntaxfehler (und einige semantische) abfange; Dann synchronisiere ich den Code mit dem Bereitstellungsserver, der unter Ubuntu Linux ausgeführt wird, und natürlich ist die Umgebung völlig anders. Es muss also unbedingt herausgefunden werden, wo sich die Dateipfade befinden, um Vorlagen, Konfigurationsdateien und statische Dateien ordnungsgemäß zu laden Dateien, etc ...
Gwyneth Llewelyn
4

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

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

Vollständige Demo:

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
quelle
4

Manchmal reicht dies aus, das erste Argument ist immer der Dateipfad

package main

import (
    "fmt"
    "os"
)


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

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

Gustavo Niemeyers Antwort ist großartig. In Windows befindet sich der Laufzeitprozess jedoch meist in einem anderen Verzeichnis:

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

Wenn Sie beispielsweise einen relativen Dateipfad verwenden, "/config/api.yaml"wird Ihr Projektpfad verwendet, in dem Ihr Code vorhanden ist.

flypapi
quelle