Introduction
Since Golang is a compiled language (non-scripted language), it will be a very painful thing if you want to get the current execution directory in a Golang program. In the past, the most compromise solution was to manually pass the path to the program by starting parameter transfer or environment variables. Today, I discovered a new solution when I was looking at the log library.
Two different ways of execution of Go programs
There are two ways to execute programs written in Go, go run and go build
The usual practice is that go run is used for local development, and it is really convenient to quickly test the code in one command; when deploying a production environment, we will build a binary file through go build and upload it to the server before executing.
What problems will arise between the two startup methods?
So what problems will arise when obtaining the current execution path under the two startup methods?
Without further ado, let's just upload the code
We write a method to get the path to the current executable file
package main import ( "fmt" "log" "os" "path/filepath" ) func main() { ("getCurrentAbPathByExecutable = ", getCurrentAbPathByExecutable()) } // Get the absolute path where the current executor is locatedfunc getCurrentAbPathByExecutable() string { exePath, err := () if err != nil { (err) res, _ := ((exePath)) return res
First start with go run
D:\Projects\demo>go run getCurrentAbPathByExecutable = C:\Users\XXX\AppData\Local\Temp\go-build216571510\b001\exe
Try go build execution again
D:\Projects\demo>go build & getCurrentAbPathByExecutable = D:\Projects\demo
By comparing the execution results, we found that we obtained different paths in two execution methods. And it is obvious that the path obtained by go run is wrong.
Reason: This is because go run will compile the source code into the system TEMP or TMP environment variable directory and start execution; go build will only compile the executable file in the current directory and will not be automatically executed.
We can simply understand that go run is equivalent to go build & ./main
Although both execution methods are ultimately the same process: source code -> compilation -> executable file -> execution output, their execution directories are completely different.
A new plan is born
This is something I suddenly realized when I was checking the service log (zap library) today. For example, the following is a simple log, and the service is started through go run, but the log library prints out my correct program path D:/Projects/te-server/modules/es/:139.
2021-03-26 17:47:06 D:/Projects/te-server/modules/es/:139 update es index {"index": "tags", "data": "[200 OK] {"acknowledged":true}"}
So I immediately looked through the zap source code and found that it was implemented through (). In fact, all Golang log libraries will have this call ().
I was happy to think that I had found the final answer, and then I wrote the code and tried it:
package main import ( "fmt" "path" "runtime" ) func main() { ("getCurrentAbPathByCaller = ", getCurrentAbPathByCaller()) } // Get the absolute path of the current execution file (go run)func getCurrentAbPathByCaller() string { var abPath string _, filename, _, ok := (0) if ok { abPath = (filename) return abPath
First try go run and go build under Windows
D:\Projects\demo>go run getCurrentAbPathByCaller = D:/Projects/demo D:\Projects\demo>go build & getCurrentAbPathByCaller = D:/Projects/demo
Well~~ The result is completely correct!
Then I threw the built program into linux and ran it, and it printed out the path to my windows --!
[root@server app]# chmod +x demo [root@server app]# ./demo getCurrentAbPathByCaller = D:/Projects/demo
I didn't expect to be happy for nothing. At this time, I was thinking that since you can get the correct result through () when go run, you can also get the correct path through () when go build;
Then if I can determine whether the current program is executed through go run or go build and choose a different path to obtain it, will all the problems be solved?
Distinguish whether the program is go run or go build execution
Go does not provide an interface to distinguish whether the program is go run or go build execution, but we can implement it in another way:
According to the execution principle of go run, we learned that it will compile the source code into the system TEMP or TMP environment variable directory and start execution;
Then we can directly compare whether the path obtained by () is the same as the path set by the environment variable TEMP. If the same, it means that it is started through go run because the current execution path is in the TEMP directory; if it is different, it is naturally the way to start go build.
Here is the complete code:
package main import ( "fmt" "log" "os" "path" "path/filepath" "runtime" "strings" ) func main() { ("getTmpDir(Current system temporary directory) = ", getTmpDir()) ("getCurrentAbPathByExecutable(Supported onlygo build) = ", getCurrentAbPathByExecutable()) ("getCurrentAbPathByCaller(Supported onlygo run) = ", getCurrentAbPathByCaller()) ("getCurrentAbPath(Final plan-Fully compatible) = ", getCurrentAbPath()) } // Final solution - fully compatiblefunc getCurrentAbPath() string { dir := getCurrentAbPathByExecutable() if (dir,getTmpDir()) { return getCurrentAbPathByCaller() return dir // Get the system temporary directory, compatible with go runfunc getTmpDir() string { dir := ("TEMP") if dir == "" { dir = ("TMP") res, _ := (dir) return res // Get the absolute path of the current execution filefunc getCurrentAbPathByExecutable() string { exePath, err := () if err != nil { (err) res, _ := ((exePath)) // Get the absolute path of the current execution file (go run)func getCurrentAbPathByCaller() string { var abPath string _, filename, _, ok := (0) if ok { abPath = (filename) return abPath
Execute in windows
D:\Projects\demo>go run getTmpDir(Current system temporary directory) = C:\Users\XXX\AppData\Local\Temp getCurrentAbPathByExecutable(Supported onlygo build) = C:\Users\XXX\AppData\Local\Temp\go-build456189690\b001\exe getCurrentAbPathByCaller(Supported onlygo run) = D:/Projects/demo getCurrentAbPath(Final plan-Fully compatible) = D:/Projects/demo
D:\Projects\demo>go build & getTmpDir(Current system temporary directory) = C:\Users\XXX\AppData\Local\Temp getCurrentAbPathByExecutable(Supported onlygo build) = D:\Projects\demo getCurrentAbPathByCaller(Supported onlygo run) = D:/Projects/demo getCurrentAbPath(Final plan-Fully compatible) = D:\Projects\demo
Upload to Linux after compilation
[root@server app]# pwd /data/app [root@server app]# ./demo getTmpDir(Current system temporary directory) = . getCurrentAbPathByExecutable(Supported onlygo build) = /data/app getCurrentAbPathByCaller(Supported onlygo run) = D:/Projects/demo getCurrentAbPath(Final plan-Fully compatible) = /data/app
Comparing the results, we can see that in different systems, different execution methods, the correct results we encapsulated getCurrentAbPath method ultimately outputs, perfect!
This is the article about Golang getting the absolute path of the current project. For more information about Golang, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!