Jak uzyskać katalog aktualnie uruchomionego pliku?

239

W nodejs używam __dirname . Co to jest równoważne w Golang?

Poszukałem go i znalazłem ten artykuł http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Gdzie używa poniżej kodu

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

Ale czy jest to właściwy sposób, czy idiomatyczny sposób w Golang?

ekanna
źródło
to nie jest odpowiedź na twoje pytanie, ale możesz buforować ścieżkę do globalnego var (lokalizacja pliku nie może zostać zmieniona podczas uruchamiania :)), aby nie uruchamiać os.open za każdym razem, gdy twój kod działa
oguzalb
Powinieneś przekazać 0, nie 1, do runtime.Caller().
fiatjaf
4
runtime.Caller(0)poda ścieżkę do pliku źródłowego, np $GOPATH/src/packagename/main.go. Inne odpowiedzi w tym wątku próbują zwrócić ścieżkę pliku wykonywalnego (jak $GOPATH/bin/packagename).
fiatjaf
Zakładasz,
1
Możliwy duplikat Go: znajdź ścieżkę do pliku wykonywalnego
Jocelyn

Odpowiedzi:

221

To powinno to zrobić:

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
źródło
2
Czy to możliwe, że wystąpił tutaj błąd? Jeśli tak, jaki byłby błąd, tylko z ciekawości?
Jeff Escalante
4
Nie działa dla mnie play.golang.org/p/c8fe-Zm_bH - os.Args [0] niekoniecznie zawiera ścieżkę abs.
zupa
5
W rzeczywistości działa, nawet jeśli os.Args [0] nie zawiera ścieżki abs. Powodem, dla którego wynik placu zabaw nie jest taki, jakiego się spodziewałeś, ponieważ znajduje się w piaskownicy.
Gustavo Niemeyer,
37
To nie jest niezawodny sposób , zobacz odpowiedź na temat korzystania z osext, ponieważ była to implementacja, która działała ze wszystkimi naszymi klientami na różnych systemach operacyjnych. Zaimplementowałem kod za pomocą tej metody, ale wydaje się, że nie jest zbyt niezawodny, a wielu użytkowników skarżyło się na błędy spowodowane przez tę metodę wybrania niewłaściwej ścieżki do pliku wykonywalnego.
JD D
5
Uzyskał taki sam wynik jak emrah przy użyciu Go 1.6 w systemie Windows (ścieżka dostępu do folderu tymczasowego zamiast folderu plików źródłowych). Aby uzyskać ścieżkę folderu pliku źródłowego bez użycia jakiegokolwiek uzależnienia zewnętrznego, należy użyć nieco spokojniejsza zmodyfikowaną wersję kodu PO za: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(uwaga runtime.Caller(0), zamiast runtime.Caller(1))
TanguyP
295

EDYCJA: Od wersji 1.8 (wydanej w lutym 2017 r.) Zalecanym sposobem jest os.Executable:

func Executable() (string, error)

Plik wykonywalny zwraca nazwę ścieżki do pliku wykonywalnego, który rozpoczął bieżący proces. Nie ma gwarancji, że ścieżka nadal wskazuje poprawny plik wykonywalny. Jeśli do uruchomienia procesu użyto dowiązania symbolicznego, w zależności od systemu operacyjnego, rezultatem może być dowiązanie symboliczne lub wskazana przez niego ścieżka. Jeśli potrzebny jest stabilny wynik, ścieżka / filepath.EvalSymlinks może pomóc.

Aby uzyskać tylko katalog pliku wykonywalnego, możesz użyć path/filepath.Dir.

Przykład :

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

STARA ODPOWIEDŹ:

Powinieneś być w stanie używać os.Getwd

func Getwd() (pwd string, err error)

Getwd zwraca zrootowaną nazwę ścieżki odpowiadającą bieżącemu katalogowi. Jeśli do bieżącego katalogu można dotrzeć wieloma ścieżkami (z powodu dowiązań symbolicznych), Getwd może zwrócić dowolną z nich.

Na przykład:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}
Internet
źródło
3
To jest bieżący katalog roboczy procesu. W nodejs jest to odpowiednik process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna
2
Ok, widzę to rozróżnienie. Jeśli chodzi o lokalizację pliku binarnego w pliku systemowym (a nie w bieżącym katalogu roboczym), myślę, że runtime.Callerjest to najbliższe „idiomatic”
Intermernet
3
„Wydany w lutym 2017 r.”? Wygląda na to, że wehikuł czasu został wynaleziony, a my mamy członków wysyłających posty z przyszłości. Miło jest wiedzieć, że przyszła wersja będzie miała niezawodną metodę wieloplatformową. W międzyczasie musimy trzymać się obecnie dostępnych rozwiązań.
ljgww
1
@ljgww Przepraszam, wezmę mojego Deloreana i wrócę do domu :-) Zaktualizowałem swoją odpowiedź z wyprzedzeniem, ponieważ właśnie zobaczyłem tę nadchodzącą funkcję i pomyślałem, że zapomnę zaktualizować odpowiedź później.
Intermernet,
1
Edytowane za pomocą, path/filepath.Dirponieważ path.Dirdziała tylko z ukośnikami (styl uniksowy) jako separatorami katalogów.
Jocelyn
63

Użyj pakietu osext

Zapewnia funkcję, ExecutableFolder()która zwraca bezwzględną ścieżkę do folderu, w którym znajduje się aktualnie wykonywalny program (przydatny do zadań cron). Jest wieloplatformowy.

Dokumentacja 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
źródło
13
To jedyna odpowiedź, która przyniosła mi oczekiwane wyniki zarówno w systemie Windows, jak i Linux.
DannyB
3
Działa to dobrze, dopóki nie chcesz go używać go run main.godo rozwoju lokalnego. Nie wiem, jak najlepiej to obejść bez wcześniejszego budowania pliku wykonywalnego.
Derek Dowling
1
Przepraszam, nie wiem jak to zrobić go run. Te pliki binarne są za każdym razem umieszczane w folderze tymczasowym.
Dobrosław Żybort
2
@DerekDowling sposób będzie najpierw zrobić go install, a następnie uruchomić go build -v *.go && ./main. -vPowie ci, które pliki są budowane. Ogólnie stwierdziłem, że czas różni się od go runi go buildjest do zaakceptowania, jeśli już biegnę go install. Dla użytkowników systemu Windows w programie PowerShell polecenie togo build -v {*}.go && ./main.exe
kumarharsh
Skoro to wróci $GOPATH/bin/, dlaczego nie skorzystać $GOPATH/bin/?
fiatjaf
10
filepath.Abs("./")

Abs zwraca absolutną reprezentację ścieżki. Jeśli ścieżka nie jest bezwzględna, zostanie połączona z bieżącym katalogiem roboczym, aby przekształcić ją w ścieżkę bezwzględną.

Jak stwierdzono w komentarzu, zwraca katalog, który jest obecnie aktywny.

Kwasowy 9
źródło
8
Zwraca bieżący katalog, a nie katalog bieżącego pliku. Na przykład byłoby inaczej, gdyby plik wykonywalny został wywołany z innej ścieżki.
Fujii,
8

jeśli skorzystasz w ten sposób:

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

dostaniesz ścieżkę / tmp, gdy uruchomisz program przy użyciu jakiegoś IDE, takiego jak GoLang, ponieważ plik wykonywalny zapisze i uruchomi się z / tmp

myślę, że najlepszym sposobem na uzyskanie aktualnego katalogu roboczego lub „.” jest :

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

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

funkcja os.Getwd () zwróci bieżący katalog roboczy. i to wszystko bez korzystania z zewnętrznej biblioteki: D

Kawałek
źródło
To nie jest poprawne. Zwraca katalog roboczy użytkownika wykonującego proces, a nie katalog pliku. Użyj pliku filepath.abs.
PodTech.io
1
zwraca działający katalog działającego pliku wykonywalnego. wtedy jeśli używasz IDE, takiego jak goland i nie ma konfiguracji dla katalogu roboczego w opcjach kompilacji, to będzie on uruchamiany z / tmp, to jakie użycie / tmp masz dla Ciebie! ??, ale jeśli używasz os.Getwd () to zwraca ścieżkę pliku wykonywalnego .exe lub elf. not / tmp.
Bit
@ Bit Używając podstawowego szablonu debugowania w takim IDE, tak, daję to, a następnie po prostu wciśnij „Edytuj konfigurację” i wypełnij „Katalog wyjściowy”, więc zobaczysz ścieżkę „os.Args [0]”, czego chcesz
ϻαϻɾΣɀО -MaMrEzO
5

Jeśli używasz pakietu osext od kardianos i musisz testować lokalnie, jak skomentował Derek Dowling:

Działa to dobrze, dopóki nie chcesz go używać z go run main.go do rozwoju lokalnego. Nie wiem, jak najlepiej to obejść bez wcześniejszego budowania pliku wykonywalnego.

Rozwiązaniem tego jest utworzenie narzędzia gorun.exe zamiast używania go run. Narzędzie gorun.exe skompiluje projekt przy użyciu „go build”, a następnie uruchomi go zaraz po tym, w normalnym katalogu projektu.

Miałem ten problem z innymi kompilatorami i odkryłem, że robię te narzędzia, ponieważ nie są one dostarczane z kompilatorem ... jest to szczególnie tajemnicze z narzędziami takimi jak C, gdzie musisz skompilować i połączyć, a następnie uruchomić (zbyt dużo pracy).

Jeśli komuś spodoba się mój pomysł na gorun.exe (lub elfa), prawdopodobnie wkrótce załaduję go do github ..

Przepraszamy, ta odpowiedź ma charakter komentarza, ale nie mogę komentować, ponieważ nie mam jeszcze wystarczająco dużej reputacji.

Alternatywnie, „go run” można zmodyfikować (jeśli nie ma już tej funkcji), aby mieć parametr taki jak „go run -notemp”, aby nie uruchamiać programu w katalogu tymczasowym (lub coś podobnego). Ale wolałbym po prostu wpisywać gorun lub „gor”, ponieważ jest on krótszy niż zwinięty parametr. Gorun.exe lub gor.exe musiałby zostać zainstalowany w tym samym katalogu, co kompilator go

Wdrożenie gorun.exe (lub gor.exe) byłoby trywialne, ponieważ zrobiłem to z innymi kompilatorami w zaledwie kilku wierszach kodu ... (słynne ostatnie słowa ;-)

Another Prog
źródło
3
Jeśli chcesz, aby zarówno działał z „uruchom” i wbudowanym plikiem wykonywalnym, po prostu użyj _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)zamiast tego
Jocelyn
@Jocelyn, twój komentarz jest tak świetny, że powinieneś zrobić z niego pełną odpowiedź! To z pewnością załatwiło sprawę - na własnej instalacji mam lokalną kopię środowiska w macOS, której używam głównie do wychwytywania błędów składniowych (i kilku semantycznych); następnie synchronizuję kod z serwerem wdrażania, który działa pod Ubuntu Linux, i oczywiście środowisko jest zupełnie inne ... więc naprawdę trzeba dowiedzieć się, gdzie ścieżki plików mają poprawnie ładować szablony, pliki konfiguracyjne, statyczne pliki itp.
Gwyneth Llewelyn
4

Czasami to wystarczy, pierwszym argumentem zawsze będzie ścieżka do pliku

package main

import (
    "fmt"
    "os"
)


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

    // or
    dir, _ := os.Getwd()
    fmt.Println(dir)
}
Yitzchak Weingarten
źródło
0

Odpowiedź Gustavo Niemeyera jest świetna. Ale w systemie Windows proces uruchomieniowy jest przeważnie w innym katalogu, takim jak ten:

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

Jeśli użyjesz względnej ścieżki pliku, na przykład "/config/api.yaml", użyje ścieżki projektu, w której istnieje kod.

flypapi
źródło