可执行文件能否动态解析其在文件系统上的位置或其实际 "resting place" 与用户的工作目录?

Can an executable dynamically resolve its location on the filesystem or its actual "resting place" vs just the working directory of the user?

如果我在 /usr/bin 中有一个可执行文件并在我位于 ~/dev/wikis/(即 user@HAL:~/dev/wikis$ the_executable)时调用它,则可执行文件中的 ioutil.ReadFile("file.txt") 函数将在 /home/user/dev/wikis/file.txt 中查找,但是否可以在用户或开发人员事先不知道可执行文件将位于 /usr/bin 中的情况下使其在 /usr/bin/file.txt 中查找(它也可以是位于 /home/user/dev/my_program/the_executable)?

然后添加一层复杂性,另一种情况,假设我从 符号 link 中调用可执行文件 /usr/bin 38=] 的可执行文件实际上在 /home/user/dev/my_program/the_executable 中,我希望程序在这种情况下动态地了解 /home/user/dev/my_program/,而不是 /usr/bin.

简而言之: 可执行文件如何动态解析其在文件系统上的位置或其实际 "resting place" 与用户的工作目录(可以轻松获取)通过 os.Getwd() 以及 ioutil.ReadFile 等其他命令使用或使用类似的东西来解析路径)。

我最好的选择是我必须获得 运行 程序的 PID (os.Getpid),然后以某种方式使用该整数来访问有关程序实例的信息 运行 在该 PID 下,希望该信息包含其目录的字符串,然后我可以使用它。

这将取决于语言,但据我所知,大多数编程语言都有办法获取程序的 'basename',例如helloworld,或完整路径,例如/usr/bin/helloworld。这通常作为程序的第一个参数提供,由 OS/runtime library/interpreter 等插入。例如,在 C 中,argv[0](因为我们在 C 中从 0 开始计数)给出当前程序的名称,但调用进程会初始化此特殊参数,因此确切的格式可能会有所不同,并且在 bash 中,$0 将扩展为执行时给定的脚本路径(我认为)。来自这里: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?

在 Linux 中(也许在其他 Unixy 系统中)你会发现一个符号 link 到实际的可执行文件 运行 作为 pid/proc/pid/exe,如果它是一个二进制文件,检查它是否会给出你想要的。如果它是某种脚本,那可能只会给解释器。

但请注意,完全有可能在 运行 时启动进程并删除可执行文件,留下悬空的 link 或什么也没有。

导出的变量os.Args(它是一个切片:[]string)保存程序参数,它的第一个元素是带有完整路径的可执行文件名。

如果可执行文件不是 symlink,您可以使用 path or filepath 包来获取可执行文件的文件夹,如下所示:

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

并且您可以使用 os.Readlink() 来解析符号 link。

并且要测试您的可执行文件是否是 symlink,您可以使用 os.Lstat() which makes no attempt to follow a link (as opposed to os.Stat()).

所以你的最终版本应该是这样的:

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