英文:
How to call the binary from a different path and open a file
问题
问题:如何解决这个问题?
回答:要解决这个问题,你可以尝试以下几种方法:
-
使用绝对路径:在
loadConfigFile()
函数中,将ConfigFileEnv
的值改为配置文件的绝对路径,而不是相对路径。例如,你可以将其设置为/home/user/Documents/ip-updater-opendns/config.json
。 -
使用相对路径:如果你希望程序在任何目录下都能正常工作,可以使用相对于可执行文件的路径。你可以使用
os.Executable()
函数获取可执行文件的路径,并将其与配置文件的相对路径拼接起来。func loadConfigFile() Config { const ConfigFileRel = "config.json" var config Config exePath, err := os.Executable() if err != nil { panic(fmt.Errorf("couldn't get executable path: %w", err)) } configFile := filepath.Join(filepath.Dir(exePath), ConfigFileRel) file, err := os.Open(configFile) // ... }
这样,程序就可以在任何目录下正确地找到配置文件。
-
设置工作目录:在程序开始时,可以使用
os.Chdir()
函数将工作目录更改为配置文件所在的目录。这样,无论从哪个目录运行程序,它都会将工作目录更改为正确的路径。func main() { exePath, err := os.Executable() if err != nil { panic(fmt.Errorf("couldn't get executable path: %w", err)) } configFileDir := filepath.Dir(exePath) err = os.Chdir(configFileDir) if err != nil { panic(fmt.Errorf("couldn't change working directory: %w", err)) } // ... }
这样,程序在运行之前会将工作目录更改为配置文件所在的目录,确保能够正确读取配置文件。
希望这些方法能够帮助你解决问题!
英文:
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?
答案1
得分: 2
当你在Go项目中运行时,config.json
文件位于当前目录,因此相对路径config.json
有效。当你从其他目录运行时,这个条件不再成立,无法找到config.json
。
const ConfigFileEnv = "config.json"
你硬编码了配置文件的名称,所以它必须在当前工作目录中。为什么不将配置文件的名称作为程序的参数呢?
if len(os.Args) < 2 {
panic("First argument should be config file path")
}
var ConfigFileEnv = os.Args[1]
当你从项目目录运行时,可以使用相对路径:
./main config.json
当你从不同的目录(或者cron)运行时,可以使用相对于主目录的不同路径:
Documents/ip-updater-opendns/main Documents/ip-updater-opendns/config.json
(注意,在路径前面添加./
一次或多次是多余的。相对路径始终相对于.
,即当前工作目录)。
另一种方法是在执行程序之前,cron作业在shell中更改目录:
*/5 * * * * bash -c "cd Documents/ip-updater-opendns/; exec ./main"
但我不会这样做。我对硬编码配置文件名的价值表示怀疑。将config.json
限制为只能从当前目录读取并没有太多意义。我会将其作为参数或环境变量传递。这样,如果你有多个OpenDNS域名,你还可以配置多个不同的配置文件。
进一步考虑,将config.json
放在项目目录中真的有很多意义吗?将其放在其他地方,比如~/.opendns-config.json
或者/var
目录中可能更合理。一旦Go程序被编译,项目目录就变得不太相关了。我可能会将main
放在/usr/local/bin/
之类的地方,并给它一个比main
更好的名称,比如opendns-update
之类的。然后cron看起来像这样:
*/5 * * * * opendns-update /home/LUCAS.VARELA/.opendns-config.json
你还可以查看https://stackoverflow.com/questions/24069664/what-does-go-install-do,了解如何使go install
适用于你的项目,这将进一步区分开发项目和在“生产”中使用的项目。
英文:
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 https://stackoverflow.com/questions/24069664/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".
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论