¿Cómo obtener el directorio del archivo actualmente en ejecución?

239

En nodejs uso __dirname . ¿Cuál es el equivalente de esto en Golang?

Busqué en Google y descubrí este artículo http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ . Donde usa el siguiente código

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

¿Pero es la forma correcta o idiomática de hacer en Golang?

ekanna
fuente
esta no es una respuesta a su pregunta, pero puede almacenar en caché la ruta a una variable global (la ubicación de su archivo no se puede cambiar mientras se ejecuta :)) para no ejecutar os.open una y otra vez cada vez que se ejecuta su código
oguzalb
Deberías pasar 0, no 1, a runtime.Caller().
fiatjaf
44
runtime.Caller(0)le dará la ruta del archivo fuente, como $GOPATH/src/packagename/main.go. Las otras respuestas en este hilo están tratando de devolver la ruta del ejecutable (como $GOPATH/bin/packagename).
fiatjaf
Estás asumiendo que el programa se está ejecutando desde un archivo ...
Flimzy
1
Posible duplicado de Go: encuentra la ruta al ejecutable
Jocelyn

Respuestas:

221

Esto debería hacerlo:

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
fuente
2
¿Es posible que haya un error aquí? Si es así, ¿cuál sería el error, solo por curiosidad?
Jeff Escalante
44
No funciona para mí play.golang.org/p/c8fe-Zm_bH - os.Args [0] no necesariamente contiene la ruta del abs.
zupa
55
Realmente funciona incluso si os.Args [0] no contiene la ruta de abs. La razón por la cual el resultado del patio de recreo no es lo que esperaba es porque está dentro de una caja de arena.
Gustavo Niemeyer
37
Esta no es una forma confiable , vea la respuesta sobre el uso de osext ya que esta fue una implementación que funcionó con todos nuestros clientes en varios sistemas operativos. Había implementado código usando este método, pero parece no ser muy confiable y muchos usuarios se quejaron de los errores causados ​​por este método al elegir la ruta incorrecta para el ejecutable.
JD D
55
Obtuve el mismo resultado que emrah usando Go 1.6 en Windows (obtuve la ruta de la carpeta temporal en lugar de la carpeta del archivo fuente). Para obtener la ruta de la carpeta de su archivo fuente sin usar ninguna dependencia externa, use una versión ligeramente modificada del código del OP: _, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)(tenga runtime.Caller(0)en cuenta el en lugar de runtime.Caller(1))
TanguyP
295

EDITAR: A partir de Go 1.8 (lanzado en febrero de 2017), la forma recomendada de hacerlo es con os.Executable:

func Executable() (string, error)

El ejecutable devuelve el nombre de ruta para el ejecutable que inició el proceso actual. No hay garantía de que la ruta todavía apunte al ejecutable correcto. Si se utilizó un enlace simbólico para iniciar el proceso, dependiendo del sistema operativo, el resultado podría ser el enlace simbólico o la ruta a la que apuntaba. Si se necesita un resultado estable, path / filepath.EvalSymlinks podría ayudar.

Para obtener solo el directorio del ejecutable que puede usar path/filepath.Dir.

Ejemplo :

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

ANTIGUA RESPUESTA:

Deberías poder usar os.Getwd

func Getwd() (pwd string, err error)

Getwd devuelve un nombre de ruta rooteado correspondiente al directorio actual. Si se puede acceder al directorio actual a través de múltiples rutas (debido a enlaces simbólicos), Getwd puede devolver cualquiera de ellas.

Por ejemplo:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}
Intermernet
fuente
3
Este es el directorio de trabajo del proceso actual. En nodejs es equivalente a process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna
2
Ok, veo la distinción. Después de la ubicación del archivo binario en el sistema de archivos (en lugar del directorio de trabajo actual), creo que runtime.Calleres lo más cercano a "idiomático"
Intermernet
3
¿Lanzado en febrero de 2017? Parece que la máquina del tiempo ha sido inventada y tenemos miembros publicando desde el futuro. Es bueno saber que una versión futura tendrá un método multiplataforma confiable. Mientras tanto, debemos apegarnos a las soluciones disponibles actualmente.
ljgww
1
@ljgww Lo siento, me llevaré mi Delorean y me iré a casa :-) Actualicé mi respuesta de antemano porque solo había visto esa próxima función y pensé que me olvidaría de actualizar la respuesta más adelante.
Intermernet
1
Editado con path/filepath.Dirporque path.Dirsolo funciona con barras diagonales (estilo Unix) como separadores de directorios.
Jocelyn
63

Usar paquete osext

Proporciona una función ExecutableFolder()que devuelve una ruta absoluta a la carpeta donde reside el ejecutable del programa que se está ejecutando actualmente (útil para trabajos cron). Es multiplataforma.

Documentación en línea

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
fuente
13
Esta es la única respuesta que produjo los resultados esperados para mí tanto en Windows como en Linux.
DannyB
3
Esto funciona bien hasta que desee utilizarlo go run main.gopara el desarrollo local. No estoy seguro de cuál es la mejor manera de evitar eso sin crear un ejecutable de antemano cada vez.
Derek Dowling
1
Lo siento, no sé cómo hacerlo funcionar go run. Estos binarios se colocan en una carpeta temporal cada vez.
Dobrosław Żybort
2
@DerekDowling una forma sería primero hacer un go install, luego correr go build -v *.go && ./main. El -vle diría qué archivos se están construyendo. En general, he descubierto que el tiempo es diferente go runy go buildtolerable si ya lo he ejecutado go install. Para los usuarios de Windows en powershell, el comando serágo build -v {*}.go && ./main.exe
kumarharsh el
Como esto volverá $GOPATH/bin/, ¿por qué no usarlo $GOPATH/bin/?
fiatjaf
10
filepath.Abs("./")

Abs devuelve una representación absoluta de ruta. Si la ruta no es absoluta, se unirá con el directorio de trabajo actual para convertirla en una ruta absoluta.

Como se indicó en el comentario, esto devuelve el directorio que está actualmente activo.

Ácido9
fuente
8
Esto devuelve el directorio actual, no el directorio del archivo actual. Por ejemplo, esto sería diferente si el ejecutable fuera llamado desde una ruta diferente.
Fujii
8

si usas de esta manera:

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

obtendrá la ruta / tmp cuando esté ejecutando el programa usando algún IDE como GoLang porque el ejecutable guardará y se ejecutará desde / tmp

Creo que la mejor manera de obtener el directorio de trabajo actual o '.' es :

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

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

la función os.Getwd () devolverá el directorio de trabajo actual. y todo sin usar ninguna biblioteca externa: D

Poco
fuente
Esto no es correcto. Esto devuelve el directorio de trabajo del usuario que ejecuta el proceso y no el directorio del archivo. Use filepath.abs.
PodTech.io
1
devuelve el directorio de trabajo del archivo ejecutable en ejecución. luego, si está utilizando un IDE como goland y no hay una configuración para el directorio de trabajo en las opciones de compilación, se ejecutará desde / tmp, ¿qué uso tiene / tmp para usted? ?? pero si usa os.Getwd () devuelve la ruta del archivo ejecutable .exe o elf. no / tmp.
Bit
@Bit Usando la plantilla base de depuración en un IDE así, sí, déle eso, luego simplemente presione 'Editar configuración' y complete 'Directorio de salida', para que vea la ruta 'os.Args [0]' es lo que desea
ϻαϻɾΣɀО -MaMrEzO
5

Si usa el paquete osext de kardianos y necesita probarlo localmente, como Derek Dowling comentó:

Esto funciona bien hasta que desee utilizarlo con go run main.go para el desarrollo local. No estoy seguro de cuál es la mejor manera de evitar eso sin crear un ejecutable de antemano cada vez.

La solución a esto es hacer una utilidad gorun.exe en lugar de usar go run. La utilidad gorun.exe compilará el proyecto usando "go build", luego lo ejecutará inmediatamente después, en el directorio normal de su proyecto.

Tuve este problema con otros compiladores y me encontré haciendo estas utilidades ya que no se envían con el compilador ... es especialmente arcano con herramientas como C donde tienes que compilar y vincular y luego ejecutarlo (demasiado trabajo).

Si a alguien le gusta mi idea de gorun.exe (o elf), probablemente lo subiré a github pronto ...

Lo sentimos, esta respuesta se entiende como un comentario, pero no puedo comentar porque todavía no tengo una reputación lo suficientemente grande.

Alternativamente, "go run" podría modificarse (si aún no tiene esta función) para tener un parámetro como "go run -notemp" para no ejecutar el programa en un directorio temporal (o algo similar). Pero preferiría simplemente escribir gorun o "gor", ya que es más corto que un parámetro complicado. Gorun.exe o gor.exe tendrían que instalarse en el mismo directorio que su compilador go

Implementar gorun.exe (o gor.exe) sería trivial, como lo hice con otros compiladores en solo unas pocas líneas de código ... (últimas palabras famosas ;-)

Otro prog
fuente
3
Si desea que funcione tanto con "go run" como con el ejecutable integrado, simplemente use _, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)en su lugar
Jocelyn
@Jocelyn, ¡tu comentario es tan bueno que deberías convertirlo en una respuesta completa! Esto sin duda me sirvió: en mi propia configuración, tengo una copia local del entorno en macOS, que utilizo principalmente para detectar errores de sintaxis (y algunos errores semánticos); luego sincronizo el código con el servidor de implementación, que se ejecuta en Ubuntu Linux, y, por supuesto, el entorno es completamente diferente ... por lo que existe una necesidad real de averiguar dónde están las rutas de los archivos para cargar correctamente plantillas, archivos de configuración, estáticos archivos, etc ...
Gwyneth Llewelyn
4

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

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

Demo 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
fuente
4

A veces esto es suficiente, el primer argumento siempre será la ruta del archivo

package main

import (
    "fmt"
    "os"
)


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

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

La respuesta de Gustavo Niemeyer es genial. Pero en Windows, el proceso de ejecución se encuentra principalmente en otro directorio, como este:

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

Si usa la ruta relativa del archivo, como "/config/api.yaml", esto usará la ruta del proyecto donde existe su código.

flypapi
fuente