2

If I have an executable in /usr/bin and call it while I'm located at ~/dev/wikis/ (that is user@HAL:~/dev/wikis$ the_executable), the ioutil.ReadFile("file.txt") function in the executable will look in /home/user/dev/wikis/file.txt, but is it possible to make it look in /usr/bin/file.txt instead without the user or developer knowing beforehand that the executable will be located in /usr/bin (it could just as well be located in /home/user/dev/my_program/the_executable)?

And then to add a layer of complexity, another situation, say I call the executable from a symbolic link in /usr/bin with the "source" of the executable actually being in /home/user/dev/my_program/the_executable, and I want the program to know about /home/user/dev/my_program/ in this case, dynamically, rather then /usr/bin.

In short: How can an executable dynamically resolve its location on the filesystem or its actual "resting place" vs the working directory of the user (which can easily be gotten through os.Getwd() and which other commands like ioutil.ReadFile use, or use something comparible, for resolving paths).

My best bet is that I have to get the PID of the running program (os.Getpid) and then somehow use that integer to access information about the instance of the program running under that PID, and hopefully that information contains a string of its directory which I can then use.

01AutoMonkey
  • 2,515
  • 4
  • 29
  • 47

3 Answers3

3

In Linux (and perhaps in other Unixy systems) you'll find a symbolic link to the actual executable running as pid under /proc/pid/exe, checking that one will give what you want if it is a binary. If it is a script of some sort, that will probably just give the interpreter.

But note that it is perfectly possible to start a process and delete the executable while it is running, leaving either a dangling link or just nothing.

vonbrand
  • 11,412
  • 8
  • 32
  • 52
0

This will depend on the language but as far as I know most programming languages have a way to get either the 'basename' of the program, e.g. helloworld, or the full path, e.g. /usr/bin/helloworld. Often this is provided as the first argument to the program, inserted by the OS/runtime library/interpreter, etc. For example, in C, argv[0] (as we start counting from 0 in C) gives the name of the current program, but the calling process initialises this special argument so the exact format can vary, and in bash, $0 will expand into the path of the script as given when executed (I think). From here: https://gobyexample.com/command-line-arguments, "os.Args provides access to raw command-line arguments. Note that the first value in this slice is the path to the program, and os.Args[1:] holds the arguments to the program." So it appears you don't need to worry about /proc but if you're interested, How do I find the location of the executable in C?

Community
  • 1
  • 1
stellarpower
  • 332
  • 3
  • 13
0

The exported variable os.Args (which is a slice: []string) holds the program arguments, its first element being the executable name with full path.

If the executable is not a symlink, you can use the path or filepath packages to get the folder of the executable like this:

folder := filepath.Dir(os.Args[0])

And you can use os.Readlink() to resolve a symbolic link.

And to test if your executable is a symlink, you can use os.Lstat() which makes no attempt to follow a link (as opposed to os.Stat()).

So your final version should look like this:

s := os.Args[0]
fi, err := os.Lstat(s)
if err != nil {
    panic(err) // Failed to get stats
}

// Check if it's a symlink and if so, try to resolve it
if fi.Mode()&os.ModeSymlink != 0 {
    if s, err = os.Readlink(s); err != nil {
        panic(err) // Failed to resolve symlink
    }
}
s = filepath.Dir(s) // We only want the folder part
icza
  • 389,944
  • 63
  • 907
  • 827
  • This still fails if the command was called via a relative path and the process changed directories, or if the executable was found from PATH. In the former case, there's no way to know for sure (that's why we parse `/proc`), and the latter case you can try to find the correct executable in PATH (`exec.LookPath` can do this). – JimB Oct 16 '15 at 13:37