This is a creation in Article, where the information may have evolved or changed.
Objective
GolangThere are various modes of operation, and how to properly refer to the file path becomes a question worthy of deliberation.
Take Gin-blog as an example, when we are at the root of the project, we go run main.go can run normally ( go build also normal)
[$ gin-blog]# go run main.go[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)[GIN-debug] GET /api/v1/tags --> gin-blog/routers/api/v1.GetTags (3 handlers)...
So at different levels of the directory, different ways to run, and what kind of, with our questions to learn
Problem
- Go run
We move up the directory level, go $GOPATH/src down, executego run gin-blog/main.go
[$ src]# go run gin-blog/main.go2018/03/12 16:06:13 Fail to parse 'conf/app.ini': open conf/app.ini: no such file or directoryexit status 1
- Go build, execute
./gin-blog/main
[$ src]# ./gin-blog/main2018/03/12 16:49:35 Fail to parse 'conf/app.ini': open conf/app.ini: no such file or directory
At this time you have to hit a big question mark, that's where my program reads.
We learned from the analysis that Golang the relative path is relative to the directory when the command was executed ;
Thinking
Now that we know where the problem is, we can think about doing something:)
We think that the relative path is relative to the execution of the command directory, then we get the address of the executable file, splicing together is not good?
Practice
We write a method to get the path of the current executable file
import ( "path/filepath" "os" "os/exec" "string")func GetAppPath() string { file, _ := exec.LookPath(os.Args[0]) path, _ := filepath.Abs(file) index := strings.LastIndex(path, string(os.PathSeparator)) return path[:index]}
Place it at the startup code to view the path
log.Println(GetAppPath())
We perform the following two commands separately to view the output results
- Go run
$ go run main.go2018/03/12 18:45:40 /tmp/go-build962610262/b001/exe
- Go Build
$ ./main2018/03/12 18:49:44 $GOPATH/src/gin-blog
Analysis
We focused on go run the output and found it to be the address of a temporary file, which is why?
In go help run , we can see
Run compiles and runs the main package comprising the named Go source files.A Go source file is defined to be a file ending in a literal ".go" suffix.
go runthat is, execution will put the file in the /tmp/go-build... directory, compile and run
So go run main.go the /tmp/go-build962610262/b001/exe result is not surprising, because it has run to the temp directory to execute the executable file.
That's clear enough, so let's think about what's going to happen.
- A path error occurs when a file that relies on a relative path
go runand go build not the same, one to the temporary directory execution, one can be manually executed in the compiled directory, the path will be handled differently
- Constantly
go run , constantly generating new temporary files
This is the root cause , because go run and go build the compilation of file execution path and different, the level of execution may not be the same, naturally there are a variety of strange problems to read
Solution Solutions
One, get the compiled executable file path
- Stitching the relative path of a configuration file with
GetAppPath() the results of an executable file that can be resolved go build main.go across directories (such as: ./src/gin-blog/main )
import ( "path/filepath" "os" "os/exec" "string")func GetAppPath() string { file, _ := exec.LookPath(os.Args[0]) path, _ := filepath.Abs(file) index := strings.LastIndex(path, string(os.PathSeparator)) return path[:index]}
But this way, for the go run still ineffective, this time need to remedy
- Issues that can be resolved by specifying a path by passing parameters
go run
package mainimport ( "flag" "fmt")func main() { var appPath string flag.StringVar(&appPath, "app-path", "app-path") flag.Parse() fmt.Printf("App path: %s", appPath)}
Run
go run main.go --app-path "Your project address"
Second, increase os.Getwd() the multi-layered judgment
See code read app.conf by Beego
The notation is compatible go build and executes at the project root go run , but go run not if it is executed across directories
Third, configure global system variables
We can os.Getenv get the system global variables and then stitch them with the relative paths.
- Set up a project workspace
In short, it is to set the project (application) work path, and then with the configuration files, log files and other relative paths to the relative absolute path to ensure that the path is consistent
See Gogs reading GOGS_WORK_DIR the code for stitching
- Using the system's own variables
In simple terms, the system comes with a global variable, for example $HOME , to store the configuration file $HOME/conf or /etc/conf
This way, you can store the configuration file more fixed, without having to set an environment variable .
(This morning with a sfer discussed a wave, thanks)
Expand
go testIn some scenarios you will also encounter path problems, because go test only the current directory can be executed, so when executing the test case, your execution directory is already a test directory
It will produce a similar problem.
Summary
These three solutions can be found in open source projects or presentations that are currently visible.
The pros and cons are also obvious, and I think the right solution should be selected for different projects .
What do you think, in SF, to discuss a wave?