Comment obtenir le répertoire du fichier en cours d'exécution?

239

Dans nodejs, j'utilise __dirname . Quel est l'équivalent de cela à Golang?

J'ai googlé et découvert cet article http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Où il utilise le code ci-dessous

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

Mais est-ce la bonne façon ou la façon idiomatique de faire à Golang?

ekanna
la source
ce n'est pas une réponse à votre question mais vous pouvez mettre en cache le chemin vers une variable globale (votre emplacement de fichier ne peut pas être changé pendant l'exécution :)) pour ne pas exécuter os.open encore et encore à chaque fois que votre code s'exécute
oguzalb
Vous devriez passer 0, non 1, à runtime.Caller().
fiatjaf
4
runtime.Caller(0)vous donnera le chemin du fichier source, comme $GOPATH/src/packagename/main.go. Les autres réponses de ce fil essaient de renvoyer le chemin de l'exécutable (comme $GOPATH/bin/packagename).
fiatjaf
Vous supposez que le programme s'exécute à partir d'un fichier ...
Flimzy
1
Duplication possible de Go: trouver le chemin vers l'exécutable
Jocelyn

Réponses:

221

Cela devrait le faire:

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
la source
2
Est-il possible qu'il y ait une erreur ici? Si oui, quelle serait l'erreur, juste par curiosité?
Jeff Escalante
4
Ne fonctionne pas pour moi play.golang.org/p/c8fe-Zm_bH - os.Args [0] ne contient pas nécessairement le chemin abs.
zupa
5
Cela fonctionne en fait même si os.Args [0] ne contient pas le chemin abs. La raison pour laquelle le résultat du terrain de jeu n'est pas ce que vous attendiez est qu'il se trouve à l'intérieur d'un bac à sable.
Gustavo Niemeyer
37
Ce n'est pas un moyen fiable , voyez la réponse sur l'utilisation d' osext car il s'agissait d'une implémentation qui fonctionnait avec tous nos clients sur différents systèmes d'exploitation. J'avais implémenté du code en utilisant cette méthode, mais il ne semble pas être très fiable et de nombreux utilisateurs se sont plaints de bogues causés par cette méthode qui choisissait le mauvais chemin pour l'exécutable.
JD D du
5
A obtenu le même résultat que emrah en utilisant Go 1.6 sur Windows (a obtenu le chemin du dossier temporaire au lieu du dossier du fichier source). Pour obtenir le chemin du dossier de votre fichier source sans utiliser de dépendance externe, utilisez une version légèrement modifiée du code de l'OP: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(notez le runtime.Caller(0)au lieu de runtime.Caller(1))
TanguyP
295

EDIT: à partir de Go 1.8 (sortie en février 2017), la méthode recommandée pour ce faire est la suivante os.Executable:

func Executable() (string, error)

L'exécutable renvoie le nom de chemin de l'exécutable qui a démarré le processus en cours. Il n'y a aucune garantie que le chemin pointe toujours vers l'exécutable correct. Si un lien symbolique a été utilisé pour démarrer le processus, selon le système d'exploitation, le résultat pourrait être le lien symbolique ou le chemin vers lequel il pointait. Si un résultat stable est nécessaire, path / filepath.EvalSymlinks peut aider.

Pour obtenir uniquement le répertoire de l'exécutable que vous pouvez utiliser path/filepath.Dir.

Exemple :

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

ANCIENNE RÉPONSE:

Vous devriez pouvoir utiliser os.Getwd

func Getwd() (pwd string, err error)

Getwd renvoie un nom de chemin enraciné correspondant au répertoire courant. Si le répertoire courant peut être atteint via plusieurs chemins (en raison de liens symboliques), Getwd peut renvoyer n'importe lequel d'entre eux.

Par exemple:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}
Intermernet
la source
3
Il s'agit du répertoire de travail actuel du processus. Dans nodejs, c'est équivalent à process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna
2
Ok, je vois la distinction. Si vous recherchez l'emplacement du binaire dans le système de fichiers (plutôt que le répertoire de travail actuel), je pense que runtime.Callerc'est le plus proche que vous obtiendrez "idiomatique"
Intermernet
3
'Sortie en février 2017'? Il semble que la machine à remonter le temps ait été inventée et nous avons des membres qui postent du futur. Il est bon de savoir qu'une future version aura une méthode multiplateforme fiable. En attendant, nous devons nous en tenir aux solutions actuellement disponibles.
ljgww
1
@ljgww Désolé, je vais prendre mon Delorean et rentrer à la maison :-) J'ai mis à jour ma réponse à l'avance parce que je venais juste de voir cette fonctionnalité à venir et je pensais que j'oublierais de mettre à jour la réponse plus tard.
Intermernet du
1
Modifié avec path/filepath.Dircar path.Dirne fonctionne qu'avec des barres obliques (style Unix) comme séparateurs de répertoires.
Jocelyn
63

Utiliser le package osext

Il fournit une fonction ExecutableFolder()qui renvoie un chemin absolu vers le dossier où réside l'exécutable du programme en cours d'exécution (utile pour les tâches cron). C'est multiplateforme.

Documentation en ligne

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
la source
13
C'est la seule réponse qui a produit les résultats attendus pour moi à la fois sur Windows et sur Linux.
DannyB
3
Cela fonctionne bien jusqu'à ce que vous souhaitiez l'utiliser avec go run main.gopour le développement local. Je ne sais pas comment mieux contourner cela sans construire un exécutable au préalable à chaque fois.
Derek Dowling
1
Désolé, je ne sais pas comment le faire fonctionner go run. Ces fichiers binaires sont placés à chaque fois dans un dossier temporaire.
Dobrosław Żybort
2
@DerekDowling un moyen serait d'abord de faire un go install, puis de courir go build -v *.go && ./main. Le -vvous dirait quels fichiers sont en cours de construction. En général, j'ai trouvé que le temps est différent entre go runet go buildest tolérable si j'ai déjà couru go install. Pour les utilisateurs de Windows sur PowerShell, la commande serago build -v {*}.go && ./main.exe
kumarharsh
Puisque cela reviendra $GOPATH/bin/, pourquoi ne pas utiliser $GOPATH/bin/?
fiatjaf
10
filepath.Abs("./")

Abs renvoie une représentation absolue du chemin. Si le chemin n'est pas absolu, il sera joint au répertoire de travail actuel pour le transformer en chemin absolu.

Comme indiqué dans le commentaire, cela renvoie le répertoire actuellement actif.

Acidic9
la source
8
Cela renvoie le répertoire actuel, pas le répertoire du fichier actuel. Par exemple, cela serait différent si l'exécutable était appelé à partir d'un chemin différent.
Fujii
8

si vous utilisez cette méthode:

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

vous obtiendrez le chemin / tmp lorsque vous exécutez un programme en utilisant un IDE comme GoLang parce que l'exécutable sera enregistré et exécuté à partir de / tmp

Je pense que la meilleure façon d'obtenir le répertoire de travail actuel ou '.' est :

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

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

la fonction os.Getwd () renverra le répertoire de travail actuel. et tout cela sans utiliser de bibliothèque externe: D

Bit
la source
Ce n'est pas correct. Cela renvoie le répertoire de travail de l'utilisateur exécutant le processus et non le répertoire du fichier. Utilisez filepath.abs.
PodTech.io
1
il renvoie le répertoire de travail du fichier exécutable en cours d'exécution. alors si vous utilisez un IDE comme goland et qu'il n'y a pas de configuration pour le répertoire de travail dans les options de construction, il s'exécutera à partir de / tmp, alors quelle utilisation / tmp a pour vous! ?? mais si vous utilisez os.Getwd () il renvoie le chemin du fichier exécutable .exe ou elf. pas / tmp.
Bit
@Bit En utilisant le modèle de base de débogage dans un tel IDE, oui, donnez-le, puis appuyez sur «Modifier la configuration» et remplissez «Répertoire de sortie», de sorte que vous verrez le chemin «os.Args [0]» est ce que vous voulez
ϻαϻɾΣɀО -MaMrEzO
5

Si vous utilisez le package osext par kardianos et que vous devez tester localement, comme l'a commenté Derek Dowling:

Cela fonctionne bien jusqu'à ce que vous souhaitiez l'utiliser avec go run main.go pour le développement local. Je ne sais pas comment mieux contourner cela sans construire un exécutable au préalable à chaque fois.

La solution est de créer un utilitaire gorun.exe au lieu d'utiliser go run. L'utilitaire gorun.exe compilerait le projet en utilisant «go build», puis l'exécuterait juste après, dans le répertoire normal de votre projet.

J'ai eu ce problème avec d'autres compilateurs et je me suis retrouvé à faire ces utilitaires car ils ne sont pas fournis avec le compilateur ... c'est particulièrement mystérieux avec des outils comme C où vous devez compiler et lier puis l'exécuter (trop de travail).

Si quelqu'un aime mon idée de gorun.exe (ou elf), je le téléchargerai probablement sur github bientôt ..

Désolé, cette réponse se veut un commentaire, mais je ne peux pas faire de commentaire car je n'ai pas encore une réputation assez grande.

Alternativement, "go run" pourrait être modifié (s'il n'a pas déjà cette fonctionnalité) pour avoir un paramètre tel que "go run -notemp" pour ne pas exécuter le programme dans un répertoire temporaire (ou quelque chose de similaire). Mais je préférerais simplement taper gorun ou "gor" car il est plus court qu'un paramètre alambiqué. Gorun.exe ou gor.exe devraient être installés dans le même répertoire que votre compilateur go

Implémenter gorun.exe (ou gor.exe) serait trivial, comme je l'ai fait avec d'autres compilateurs en seulement quelques lignes de code ... (derniers mots célèbres ;-)

Un autre prog
la source
3
Si vous voulez qu'il fonctionne à la fois avec "go run" et un exécutable intégré, utilisez simplement à la _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)place
Jocelyn
@Jocelyn, votre commentaire est si génial que vous devriez en faire une réponse complète! Cela a certainement fait l'affaire pour moi - sur ma propre configuration, j'ai une copie locale de l'environnement dans macOS, que j'utilise principalement pour détecter les erreurs de syntaxe (et quelques sémantiques); puis je synchronise le code avec le serveur de déploiement, qui fonctionne sous Ubuntu Linux, et bien sûr l'environnement est complètement différent ... il y a donc un réel besoin de déterminer où se trouvent les chemins d'accès aux fichiers pour charger correctement les modèles, les fichiers de configuration, les statiques fichiers, etc ...
Gwyneth Llewelyn
4

Parfois, cela suffit, le premier argument sera toujours le chemin du fichier

package main

import (
    "fmt"
    "os"
)


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

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

La réponse de Gustavo Niemeyer est excellente. Mais dans Windows, le proc d'exécution est principalement dans un autre répertoire, comme ceci:

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

Si vous utilisez un chemin de fichier relatif, comme "/config/api.yaml", cela utilisera le chemin de votre projet là où votre code existe.

flypapi
la source