Home > Back-end >  How to call the binary from a different path and open a file
How to call the binary from a different path and open a file

Time:10-25

Context

I created a simple program to auto update my Public IP address on OpenDNS. To be able to use the program I need the credentials for OpenDNS site, so I created a config.json file to be able to read the credentials from it.

Inside the Go project folder, calling the binary, works fine
./main
http return code: 200 defining the new IP: 123.123.123.123
If I change to another folder (let's say my /home/user for example), the binary can't open the file anymore
./Documents/ip-updater-opendns/main 
panic: couldn't open config file config.json: open config.json: no such file or directory

goroutine 1 [running]:
main.loadConfigFile()
        /home/user/Documents/ip-updater-opendns/main.go:50  0x1e6
main.main()
        /home/user/Documents/ip-updater-opendns/main.go:25  0x45
I'm opening the file the following way:
func loadConfigFile() Config {
    const ConfigFileEnv = "config.json"
    var config Config

    file, err := os.Open(ConfigFileEnv)
    if err != nil {
        panic(fmt.Errorf("couldn't open config file %s: %w", ConfigFileEnv, err))
    }

    err = json.NewDecoder(file).Decode(&config)
    if err != nil {
        panic(fmt.Errorf("couldn't decode config file %s as Json: %w", ConfigFileEnv, err))
    }

    return config
}
Why do I want to call my binary from another folder?

Because I want this program to be executed by cronjob every 5 minutes.

So my question is, how to solve this problem?

CodePudding user response:

When you run in your go project, config.json is in the current directory, so the relative path config.json works. When you run from other directories, that is no longer true, and config.json cannot be found.

    const ConfigFileEnv = "config.json"

You hard coded the config file name so it must be in your current working directory. Why not put the name of the configuration file in an argument to the program?

    if len(os.Args) < 1 {
      panic("First argument should be config file path")
    }
    var ConfigFileEnv = os.Args[1] 

When you run it from your project directory you can use a relative path:

./main config.json

When you run it from a different directory (or your cron) you can use a different path, relative to the home directory:

Documents/ip-updater-opendns/main Documents/ip-updater-opendns/config.json

(note that prepending ./ to a path 1 or more times is redundant. Relative paths are always relative to ., the current working directory).

The alternative would be for the cron job to change the directory in a shell before executing your program:

*/5 * * * * bash -c "cd Documents/ip-updater-opendns/; exec ./main"

But I wouldn't do that. I question the value of hard coding a config file name. It doesn't make sense to impose the limitation of only being able to read config.json from the current directory. I would pass that either as an argument or an environment variable. Then you could also configure multiple different configuration files if you happened to have multiple OpenDNS domain names.

To take that consideration one step further, does it really make a lot of sense to put config.json into your project directory? It might make more sense to put it elsewhere, like ~/.opendns-config.json or somewhere in /var perhaps. The project directory is pretty irrelevant once the go program has been compiled. I probably would put main somewhere like /usr/local/bin/ myself - and name it something better than main - maybe opendns-update or something like that. Then the cron looks somethign like:

*/5 * * * * opendns-update /home/LUCAS.VARELA/.opendns-config.json

You might also want to take a look at What does go install do? which explains how you could make go install work for your project, which would further differentiate between the development project and its use in "production".

  • Related