Windows与Linux的GCC argv[0]值比较

8
可能重复:获取可执行文件路径 我在Windows上使用MinGW,gcc 4.4.3编程。当我像这样使用main函数时:
int main(int argc, char* argv[]){
    cout << "path is " << argv[0] << endl;
}

在Windows系统下,我得到的是完整路径:"C:/dev/stuff/bin/Test"。然而,在Linux系统下运行同样的应用程序时,我得到的是一种相对路径:"bin/Test"。这破坏了我的应用程序!有没有办法确保在两个系统上路径都是绝对路径?


可能相关:https://dev59.com/SnI-5IYBdhLWcg3wBjhR 和 https://dev59.com/uXI_5IYBdhLWcg3wF_B3 - Seth Carnegie
3
我相当确定没有办法保证任何这样的事情。如果你需要一个可执行文件的绝对路径,你需要使用除了 argv[0] 之外的其他东西来获取它。 - Jerry Coffin
这在Windows上也不应该起作用。你是通过命令行还是通过资源管理器启动程序的? - Harry Johnston
我正在使用一个Makefile。Makefile命令是bin/test。很奇怪,不是吗... - Nate Glenn
3个回答

10
没有,大多数Linux上的shell都是使用argv[0]来精确获取用户输入以运行二进制文件。这允许二进制文件根据用户的输入执行不同的操作。
例如,具有几个不同命令行指令的程序可以一次安装二进制文件,然后将各种不同的命令硬链接到同一个二进制文件中。例如,在我的系统上:
$ ls -l /usr/bin/git*
-rwxr-xr-x  109 root  wheel  2500640 16 May 18:44 /usr/bin/git
-rwxr-xr-x    2 root  wheel   121453 16 May 18:43 /usr/bin/git-cvsserver
-rwxr-xr-x  109 root  wheel  2500640 16 May 18:44 /usr/bin/git-receive-pack
-rwxr-xr-x    2 root  wheel  1021264 16 May 18:44 /usr/bin/git-shell
-rwxr-xr-x  109 root  wheel  2500640 16 May 18:44 /usr/bin/git-upload-archive
-rwxr-xr-x    2 root  wheel  1042560 16 May 18:44 /usr/bin/git-upload-pack
-rwxr-xr-x    1 root  wheel   323897 16 May 18:43 /usr/bin/gitk
注意到有些文件大小完全相同。更深入的调查揭示了以下信息:
$ stat /usr/bin/git
234881026 459240 -rwxr-xr-x 109 root wheel 0 2500640 "Oct 29 08:51:50 2011" "May 16 18:44:05 2011" "Jul 26 20:28:29 2011" "May 16 18:44:05 2011" 4096 4888 0 /usr/bin/git
$ stat /usr/bin/git-receive-pack 
234881026 459240 -rwxr-xr-x 109 root wheel 0 2500640 "Oct 29 08:51:50 2011" "May 16 18:44:05 2011" "Jul 26 20:28:29 2011" "May 16 18:44:05 2011" 4096 4888 0 /usr/bin/git-receive-pack

这个文件的 inode 号(459240)是相同的,因此这两个是指向同一个磁盘上的文件链接。运行时,这个可执行文件使用argv[0]的内容来确定要执行哪个函数。你可以在 Git 的 main() 函数的代码中看到这一点(有点)。


5
无论如何,调用exec*函数族中的任一函数来运行您的代码的任何代码都可以将任何内容放入argv[0] - Adam Rosenfield

5

argv数组

argv[0]就像其他参数一样:它可以是任意以NUL结尾的字节字符串。它可以是空字符串。它是启动进程想要的任何内容。

默认情况下,shell会将argv[0]设置为用于命名程序的任何内容:在$PATH中查找的名称、相对路径或绝对路径。它可以是符号链接或常规文件。

要使用其他值调用程序,在zsh(不知道其他shell)中使用:

ARGV0=你想要的内容 some_program 参数

如果你真的需要可执行文件的路径,则不能在Unix上使用命令行。

仅适用于Linux

在Linux上:/proc/self/exe是可执行文件的符号链接。

你可以使用readlink读取它。你也可以直接使用statopen打开它。

重命名和软链接

普通的软链接只是一个愚蠢的字符串,不知道其目标发生了什么(如果目标存在)。但/proc/self/exe的软链接是魔法链接。

如果重命名,软链接将跟随重命名。如果有多个硬链接,它将遵循使用的特定硬链接的名称。(因此,在Linux下,对同一文件的不同硬链接并不完全等效。)

如果取消链接这个硬链接,我认为"(已删除)"会附加到符号链接的值上。请注意,这是一个有效的文件名,因此另一个不相关的文件可能具有该名称。

在任何情况下,符号链接是文件的硬链接,因此你可以直接使用statopen打开它。

如果在启动可执行文件的系统以外的其他系统上重命名或取消链接二进制文件,则无法在网络文件系统上保证任何内容。

安全考虑

当你的程序使用/proc/self/exe特殊文件时,启动程序所使用的文件可能会被unlinkrename。如果程序是特权程序(SUID或设置了能力),则应该认真对待此问题:即使用户没有写访问权限,也可能通过在相同文件系统上的目录中具有写访问权限来创建到原始“设置某些内容”的二进制文件的硬链接,从而可能更改正在运行的特权二进制文件的名称。

在你readlink的时候,返回的值可能会引用另一个文件。(当然,使用readlink的结果进行open始终存在不可避免的竞争条件。)

通常情况下,NFS无法提供与本地文件系统相同的所有保证。


3

无法确保argv [0]是绝对路径,因为它应该恰好是用户调用程序的方式。因此,如果在Linux命令行上通过./bin/Test调用程序,则argv [0]应该恰好是"./bin/Test"

如果当您通过命令提示符通过.\bin\Test调用程序时,argv [0]"C:/dev/stuff/bin/Test",则似乎是MinGW运行时中的一个错误。使用最新的MinGW(gcc版本4.5.2),通过.\bin\Test调用二进制文件意味着argv [0]".\bin\Test"。通过Microsoft Visual C++构建的二进制文件(cl版本16.00.40219.01)通过.\bin\Test调用也具有".\bin\Test"作为argv [0]


1
它应该与用户调用程序的方式完全相同。除非它是登录 shell,在这种情况下会添加破折号。 - ninjalj
6
C标准(5.1.2.2.1p2)指出,“如果argc的值大于零,则argv [0]指向的字符串表示程序名称”;它故意不清楚“程序名称”是什么或者argv [0]如何“表示”它。 POSIX基本上表示argv[0]是在调用程序时传递给exec*()函数的内容,这意味着它取决于调用shell。 - Keith Thompson
@ninjalj:我不知道那个。很有意思 :) - Daniel Trebbien
@KeithThompson: 好的,那么这不是一个MinGW的bug。很好知道。 - Daniel Trebbien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接