Loading files in Go

Loading files in Go

How to load a file in go irrespective of location of caller ?

This is a very common scenario of loading files in your program. For simplicity, let's assume the following path & directory structure.

path: /Users/gyanesh/foo/
.
└── foo
    ├── config.json
    └── main.go

Now, consider this snippet inside main.go which tries to load a config.json file & prints the current directory.

package main
import(
    "fmt"
    "os"
)
func LoadFromCurrentDir() {
    dir, err := os.Getwd()
    if err != nil {
        panic(err)
    }
    filePath := dir + "/config.json"
    _, err = os.Open(filePath)
    if err != nil{
        panic(err)
    }
    fmt.Println(filePath)
}

func main() {
    LoadFromCurrentDir()
}

If we run this from foo directory, it will work fine.

$> pwd
/Users/gyanesh/foo
$> go run main.go
/Users/gyanesh/foo/config.json

However, If we try to run the code from some other directory, LoadFromCurrentDir() will panic.

$> mkdir /Users/gyanesh/bar/
$> cd  /Users/gyanesh/bar/

$> go run ./../foo/main.go
panic: open /Users/bar/config.json: no such file or directory

So, os.Getwd() will give us the current directory of the place of invocation and not the current directory of the file where the code is executing at runtime.

Let's check another snippet inside /Users/foo/main.go

package main
import(
    "errors"
    "fmt"
    "os"
    "runtime"
    "strings"
)

func LoadFromFileDir() {
    _, file, _, ok := runtime.Caller(0)
    if !ok {
        panic(errors.New("couldn't load file"))
    }
    pathArr := strings.Split(file, "/")

    dir := strings.Join(pathArr[:len(pathArr)-1], "/")
    filePath := dir + "/config.json"
    _, err := os.Open(filePath)
    if err != nil{
        panic(err)
    }
    fmt.Println(filePath)
}

func main() {
    LoadFromFileDir()
}

Now, if we invoke it from any location outside the project dir, it will load the config file relative to the file where code is executing.

$> pwd
Users/gyanesh/bar/
$> go run ./../foo/main.go
/Users/foo/config.json

So, use runtime.Caller(skip int) to extract path for loading files if:

  1. Your package will be imported by any other package
  2. You plan on running it from any directory outside your package directory


References: