Como obter o diretório do arquivo em execução no momento?

239

No nodejs eu uso __dirname . Qual é o equivalente disso em Golang?

Pesquisei no Google e descobri este artigo http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Onde ele usa o código abaixo

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

Mas é o caminho certo ou idiomático para fazer em Golang?

ekanna
fonte
isso não é uma resposta para sua pergunta, mas você pode armazenar em cache o caminho para uma var global (o local do arquivo não pode ser alterado durante a execução :)) para não executar o os.open repetidamente sempre que seu código for executado
oguzalb
Você deve passar 0, não 1para runtime.Caller().
Fiatjaf
4
runtime.Caller(0)lhe dará o caminho do arquivo de origem, como $GOPATH/src/packagename/main.go. As outras respostas neste segmento estão tentando retornar o caminho do executável (como $GOPATH/bin/packagename).
Fiatjaf
Você está assumindo que o programa está sendo executado a partir de um arquivo ...
Flimzy
1
Possível duplicado de Go: encontrar o caminho para o executável
Jocelyn

Respostas:

221

Isso deve servir:

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
fonte
2
É possível que haja um erro aqui? Se sim, qual seria o erro, apenas por curiosidade?
Jeff Escalante
4
Não funciona para mim play.golang.org/p/c8fe-Zm_bH - os.Args [0] não contém necessariamente o caminho do abs.
zupa
5
Na verdade, ele funciona mesmo que os.Args [0] não contenha o caminho abs. A razão pela qual o resultado do playground não é o que você esperava é porque está dentro de uma caixa de areia.
23415 Gustavo Niemeyer
37
Esta não é uma maneira confiável ; veja a resposta sobre o uso do osext, pois foi uma implementação que funcionou com todos os nossos clientes em vários sistemas operacionais. Eu havia implementado o código usando esse método, mas ele parece não ser muito confiável e muitos usuários se queixaram de erros causados ​​por esse método, escolhendo o caminho errado para o executável.
JD D
5
Obteve o mesmo resultado que o emrah usando o Go 1.6 no Windows (obteve o caminho da pasta temp em vez da pasta do arquivo de origem). Para obter o caminho da pasta do seu arquivo de origem sem usar qualquer dependência externa, use uma versão slighly modificada do código do OP: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(note a runtime.Caller(0)vez de runtime.Caller(1))
TanguyP
295

EDIT: A partir do Go 1.8 (Lançado em fevereiro de 2017), a maneira recomendada de fazer isso é os.Executable:

func Executable() (string, error)

Executável retorna o nome do caminho do executável que iniciou o processo atual. Não há garantia de que o caminho ainda esteja apontando para o executável correto. Se um link simbólico foi usado para iniciar o processo, dependendo do sistema operacional, o resultado pode ser o link simbólico ou o caminho apontado. Se um resultado estável for necessário, path / filepath.EvalSymlinks pode ajudar.

Para obter apenas o diretório do executável, você pode usar path/filepath.Dir.

Exemplo :

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

RESPOSTA ANTIGA:

Você deve poder usar os.Getwd

func Getwd() (pwd string, err error)

Getwd retorna um nome de caminho raiz correspondente ao diretório atual. Se o diretório atual puder ser alcançado por vários caminhos (devido a links simbólicos), Getwd poderá retornar qualquer um deles.

Por exemplo:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}
Intermernet
fonte
3
Este é o diretório de trabalho do processo atual. Em nodejs é equivalente a process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna
2
Ok, eu vejo a distinção. De que você está depois a localização do binário no filesytem (em vez do diretório de trabalho atual) Eu acho que runtime.Calleré o mais próximo que você vai chegar ao "idiomático"
Intermernet
3
'Lançado em fevereiro de 2017'? Parece que a máquina do tempo foi inventada e temos membros postando no futuro. É bom saber que uma versão futura terá um método confiável de plataforma cruzada. Enquanto isso, precisamos nos ater às soluções atualmente disponíveis.
precisa saber é
1
@ljgww Desculpe, eu vou pegar o meu Delorean e vou para casa :-) Atualizei minha resposta com antecedência porque eu tinha acabado de ver o próximo recurso e percebi que esqueceria de atualizar a resposta mais tarde.
Intermernet 15/01
1
Editado com path/filepath.Dirporque path.Dirfunciona apenas com barras (estilo Unix) como separadores de diretório.
Jocelyn
63

Use o pacote osext

Ele está fornecendo uma função ExecutableFolder()que retorna um caminho absoluto para a pasta onde reside o executável do programa atualmente em execução (útil para tarefas cron). É multiplataforma.

Documentação 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
fonte
13
Esta é a única resposta que produziu os resultados esperados para mim no Windows e no Linux.
DannyB
3
Isso funciona bem até que você queira usá-lo no go run main.godesenvolvimento local. Não tenho certeza da melhor maneira de contornar isso sem criar previamente um executável a cada vez.
Derek Dowling
1
Desculpe, não sei como fazê-lo funcionar go run. Esses binários são colocados em uma pasta temporária a cada vez.
Dobrosław Żybort 18/01/16
2
@DerekDowling uma maneira seria primeiro fazer um go installe depois executar go build -v *.go && ./main. O -vdiria quais arquivos estão sendo criados. Geralmente, descobri que o tempo é diferente go rune go buildtolerável se eu já tiver executado go install. Para usuários do Windows no powershell, o comando serágo build -v {*}.go && ./main.exe
kumarharsh
Como isso retornará $GOPATH/bin/, por que não usar $GOPATH/bin/?
Fiatjaf
10
filepath.Abs("./")

Abs retorna uma representação absoluta do caminho. Se o caminho não for absoluto, ele será associado ao diretório de trabalho atual para transformá-lo em um caminho absoluto.

Conforme declarado no comentário, isso retorna o diretório que está ativo no momento.

Acidic9
fonte
8
Isso retorna o diretório atual, não o diretório do arquivo atual. Por exemplo, isso seria diferente se o executável fosse chamado de um caminho diferente.
Fujii
8

se você usar desta maneira:

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

você obterá o caminho / tmp quando estiver executando um programa usando algum IDE como o GoLang, porque o executável salvará e executará em / tmp

Eu acho que a melhor maneira de obter o diretório de trabalho atual ou '.' é :

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

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

a função os.Getwd () retornará o diretório de trabalho atual. e tudo isso sem o uso de qualquer biblioteca externa: D

Mordeu
fonte
Isso não está correto. Isso retorna o diretório de trabalho do usuário que está executando o processo e não o diretório do arquivo. Use filepath.abs.
PodTech.io 01/03
1
ele retorna o diretório de trabalho do arquivo executável em execução. então, se você estiver usando um IDE como goland e não houver nenhuma configuração para o diretório ativo nas opções de compilação, ele será executado a partir de / tmp, então que uso / tmp tem para você! ?? mas se você usar os.Getwd () retorna o caminho do arquivo executável .exe ou elf. não / tmp.
Bit
@Bit Usando o modelo básico de depuração em um IDE como esse, sim, basta clicar em 'Editar configuração' e preencher 'Diretório de saída', para que você veja o caminho 'os.Args [0]' é o que deseja
ϻαϻɾΣɀО -MaMrEzO
5

Se você usa o pacote osext da kardianos e precisa testar localmente, como comentou Derek Dowling:

Isso funciona bem até que você queira usá-lo com vá em main.go para desenvolvimento local. Não tenho certeza da melhor forma de contornar isso sem criar previamente um executável a cada vez.

A solução para isso é criar um utilitário gorun.exe em vez de usar go run. O utilitário gorun.exe compila o projeto usando "go build" e depois o executa no diretório normal do seu projeto.

Eu tive esse problema com outros compiladores e me vi criando esses utilitários, pois eles não são fornecidos com o compilador ... é especialmente arcano com ferramentas como C, nas quais você precisa compilar e vincular e depois executá-lo (muito trabalho).

Se alguém gostar da minha idéia do gorun.exe (ou elf), provavelmente o carregarei no github em breve ..

Desculpe, esta resposta é um comentário, mas não posso comentar porque ainda não tenho uma reputação grande o suficiente.

Como alternativa, "go run" pode ser modificado (se já não tiver esse recurso) para ter um parâmetro como "go run -notemp" para não executar o programa em um diretório temporário (ou algo semelhante). Mas eu preferiria apenas digitar gorun ou "gor", pois é mais curto que um parâmetro complicado. Gorun.exe ou gor.exe precisaria ser instalado no mesmo diretório do seu compilador go

A implementação de gorun.exe (ou gor.exe) seria trivial, como fiz com outros compiladores em apenas algumas linhas de código ... (famosas últimas palavras ;-)

Outro Prog
fonte
3
Se você quer que ambos trabalham com "vai executar" e construído executável então simplesmente use _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)vez
Jocelyn
@Jocelyn, seu comentário é tão bom que você deve fazer disso uma resposta completa! Isso certamente fez o truque para mim - na minha própria configuração, tenho uma cópia local do ambiente no macOS, que geralmente uso para detectar erros de sintaxe (e alguns semânticos); sincronizo o código com o servidor de implantação, que é executado no Ubuntu Linux e, é claro, o ambiente é completamente diferente ... portanto, há uma necessidade real de descobrir onde os caminhos dos arquivos devem carregar modelos, arquivos de configuração, estáticos e estáticos corretamente. arquivos, etc ...
Gwyneth Llewelyn
4

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

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

Demonstração completa:

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

Às vezes isso é suficiente, o primeiro argumento sempre será o caminho do arquivo

package main

import (
    "fmt"
    "os"
)


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

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

A resposta de Gustavo Niemeyer é ótima. Mas no Windows, o proc de tempo de execução está principalmente em outro diretório, como este:

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

Se você usar o caminho relativo do arquivo, como "/config/api.yaml", isso usará o caminho do projeto onde seu código existe.

flypapi
fonte