Introduction: "Where am I?" — the program asks
\nImagine: you are writing a Go utility that should read a config from a folder next to its executable file. Or, for example, you are creating a log file in the same directory where the program resides. It would seem, what could be simpler? But this is where the fun begins: Go is a compiled language, and it has its own "secrets" when it comes to finding out its own location.
\nThe question "How to get the directory of the currently running file?" is one of the most popular on Stack Overflow among Go beginners. And this is no accident. Unlike scripting languages, where __file__ (Python) or __dirname (Node.js) solve everything in a jiffy, in Go you have to think: which file exactly are we interested in? The source code (during compilation) or the executable binary (during runtime)?
In this article, we will cover both approaches, learn how to avoid common pitfalls, and write several Go code examples that can be used in real projects. Let's go!
\n\nTwo worlds, two childhoods: source vs binary
\nBefore writing code, let's understand the main dilemma. In Go, a program is first compiled into a static binary. When you run go run main.go, Go creates a temporary executable file and runs it. When you do go build, you get a ready-made file that can be moved to another computer.
Therefore, the question "where am I?" has two answers:
\n- \n
- Path to the source file — needed during development, for logging or tests. \n
- Path to the executable file — needed in production to find nearby resources (configs, databases, etc.). \n
Most answers on Stack Overflow relate to the second option, but we will cover both so that you are fully equipped.
\n\nMethod #1: Finding the directory of the running binary
\nThis is the most common scenario. Your program is already compiled and running, and it needs to understand which folder it was launched from. Go provides an elegant way for this — the os package and the os.Executable() function.
Code example: basic version
\npackage main\n\nimport (\n "fmt"\n "os"\n "path/filepath"\n)\n\nfunc main() {\n // Get the full path to the executable file\n exePath, err := os.Executable()\n if err != nil {\n fmt.Println("Error getting path:", err)\n return\n }\n fmt.Println("Full path to binary:", exePath)\n\n // Extract the directory\n exeDir := filepath.Dir(exePath)\n fmt.Println("Executable file directory:", exeDir)\n}\nRun this code via go run main.go — you will see a path like /tmp/go-build123456/b001/exe/main. This is a temporary directory. But if you compile (go build -o myapp) and run ./myapp, you will get the current folder.
Pitfalls: symlinks and chdir
\nThe os.Executable() function returns the path by which the program was launched. But there are nuances:
- \n
- Symbolic links. If you launched the program via a symlink (
/usr/local/bin/myapp -> /opt/myapp_v2/myapp),os.Executable()will return the path to the link itself, not to the real file. To get the "real" path, usefilepath.EvalSymlinks(). \n - Changing the working directory. If the program internally calls
os.Chdir(), this does not affectos.Executable()— it will still return the path to the binary. \n
Improved version with symlink handling
\npackage main\n\nimport (\n "fmt"\n "os"\n "path/filepath"\n)\n\nfunc main() {\n exePath, err := os.Executable()\n if err != nil {\n panic(err)\n }\n\n //