为什么 argv 包含程序名称?

Shr*_*har 114 command-line c arguments

典型的 Unix/Linux 程序接受命令行输入作为参数计数 ( int argc) 和参数向量 ( char *argv[])。的第一个元素argv是程序名称 - 后跟实际参数。

为什么程序名称作为参数传递给可执行文件?是否有使用自己名称的程序示例(可能是某种exec情况)?

cou*_*ode 131

首先,请注意这argv[0]不一定是程序名称。这是调用者argv[0]execve系统调用中输入的内容(例如,请参阅Stack Overflow 上的这个问题)。( 的所有其他变体exec都不是系统调用,而是到 的接口execve。)

例如,假设以下内容(使用execl):

execl("/var/tmp/mybackdoor", "top", NULL);
Run Code Online (Sandbox Code Playgroud)

/var/tmp/mybackdoor是执行但argv[0]设置为 的内容top,这就是ps或(真实的)top将显示的内容。有关更多信息,请参阅U&L SE 上的此答案

设置所有的这一边:花哨的文件系统的出现和以前一样/procargv[0]是一个过程,了解了自己的名字的唯一途径。那有什么用呢?

  • 几个程序根据调用它们的名称自定义它们的行为(通常通过符号或硬链接,例如BusyBox 的实用程序;此问题的其他答案中提供了更多示例)。
  • 此外,通过 syslog 登录的服务、守护进程和其他程序通常会将其名称添加到日志条目中;没有这个,事件跟踪将变得几乎不可行。

  • busybox 是另一个很好的例子。可以通过308个不同的名字来调用不同的命令:https://busybox.net/downloads/BusyBox.html#commands (19认同)
  • 此类程序的示例是`bunzip2`、`bzcat` 和`bzip2`,其中前两个是指向第三个的符号链接。 (18认同)
  • @Ruslan 有趣的是,`zcat` 不是符号链接。他们似乎使用 shell 脚本来避免这种技术的缺点。但是他们无法打印完整的 `--help` 输出,因为向 gzip 添加选项的人也忘记了维护 zcat。 (5认同)
  • 从我记事起,GNU 编码标准就不鼓励使用 argv[0] 来更改程序行为([当前版本中的“一般接口标准”部分](https://www.gnu.org/ prep/standards/standards.html#用户界面))。“gunzip”是一个历史例外。 (3认同)
  • 许多、更多的程序还在它们的使用/帮助输出中注入了它们的 `argv[0]`,而不是硬编码它们的名称。有些是完整的,有些只是基本名称。 (2认同)

mur*_*uru 65

大量:

  • 当是时,Bash 以POSIX 模式运行。当以.argv[0]shargv[0]-
  • Vim的行为不同,当为运行viviewevimeviewexvimdiff,等。
  • 如前所述,Busybox。
  • 在 systemd 作为 init shutdownreboot、 等的系统中, 是systemctl符号链接
  • 等等。

  • 另一种是“sendmail”和“mail”。每一个unix MTA都带有这两个命令的符号链接,旨在模拟被调用时的原始行为,这意味着任何需要发送邮件的unix程序都确切地知道他们如何做到这一点。 (7认同)
  • 另一种常见情况:`test` 和`[`:当你调用前者时,如果最后一个参数是`]`,它会处理一个错误。(在实际的 Debian 稳定版中,这些命令是两个不同的程序,但以前的版本和 MacO 仍然使用相同的程序)。还有 `tex`、`latex` 等等:二进制文件是一样的,但是看看它是如何调用的,它选择了正确的 _configuration_ 文件。`init` 类似。 (4认同)
  • 相关的,如果最后一个参数是 *not* `]`,`[` 认为它是一个错误。 (4认同)

dir*_*rkt 37

从历史上看,argv它只是一个指向命令行“单词”的指针数组,因此从第一个“单词”开始是有意义的,它恰好是程序的名称。

并且有相当多的程序根据用于调用它们的名称而表现出不同的行为,因此您可以创建指向它们的不同链接并获得不同的“命令”。我能想到的最极端的例子是busybox,它就像几十个不同的“命令”,具体取决于它的调用方式

编辑:根据要求提供 Unix 第一版的参考

例如,可以从它的主要功能中cc看到argc并且argv已经使用了。该外壳拷贝参数传递给parbuf里面的newarg循环的一部分,而在处理方式相同参数的命令本身。(当然,稍后它只执行第一个参数,即命令的名称)。好像execv那时候没有亲戚。


rud*_*ier 23

用例:

您可以使用程序名称来更改程序行为

例如,您可以创建一些指向实际二进制文件的符号链接。

使用这种技术的一个著名例子是 busybox 项目,它只安装一个二进制文件和许多符号链接。(ls、cp、mv 等)。他们这样做是为了节省存储空间,因为他们的目标是小型嵌入式设备。

这也在setarchutil-linux 中使用:

$ ls -l /usr/bin/ | grep setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 i386 -> setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 linux32 -> setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 linux64 -> setarch
-rwxr-xr-x 1 root root       14680 2015-10-22 16:54 setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 x86_64 -> setarch
Run Code Online (Sandbox Code Playgroud)

他们在这里使用这种技术基本上是为了避免许多重复的源文件或只是为了保持源代码的可读性。

另一个用例是需要在运行时加载一些模块或数据的程序。拥有程序路径使您能够从相对于程序位置的路径加载模块

此外,许多程序会打印错误消息,包括程序名称

为什么

  1. 因为它是 POSIX 约定 ( man 3p execve):

argv 是传递给新程序的参数字符串数组。按照惯例,这些字符串中的第一个应该包含与正在执行的文件相关联的文件名。

  1. 它是 C 标准(至少是 C99 和 C11):

如果argc的值大于零,则argv[0]指向的字符串代表程序名;如果宿主环境中的程序名称不可用,则 argv[0][0] 应为空字符。

注意 C 标准说的是“程序名”而不是“文件名”。

  • 如果您从另一个符号链接到达符号链接,这不会中断吗? (3认同)
  • @Mehrdad,是的,这是缺点,可能会让用户感到困惑。 (3认同)

mar*_*elm 23

除了根据调用方式改变其行为的程序之外,我发现argv[0]打印程序的用法很有用,如下所示:

printf("Usage: %s [arguments]\n", argv[0]);
Run Code Online (Sandbox Code Playgroud)

这会导致使用消息始终使用调用它的名称。如果程序被重命名,它的使用信息也会随之改变。它甚至包括调用它的路径名:

# cat foo.c 
#include <stdio.h>
int main(int argc, char **argv) { printf("Usage: %s [arguments]\n", argv[0]); }
# gcc -Wall -o foo foo.c
# mv foo /usr/bin 
# cd /usr/bin 
# ln -s foo bar
# foo
Usage: foo [arguments]
# bar
Usage: bar [arguments]
# ./foo
Usage: ./foo [arguments]
# /usr/bin/foo
Usage: /usr/bin/foo [arguments]
Run Code Online (Sandbox Code Playgroud)

这是一个很好的接触,特别是对于可能无处不在的小型专用工具/脚本。

这在 GNU 工具中似乎也很常见,请参见ls示例:

% ls --qq
ls: unrecognized option '--qq'
Try 'ls --help' for more information.
% /bin/ls --qq
/bin/ls: unrecognized option '--qq'
Try '/bin/ls --help' for more information.
Run Code Online (Sandbox Code Playgroud)

  • +1。我打算提出同样的建议。奇怪的是,这么多人专注于改变行为,却没有提到最明显和更广泛的用法。 (3认同)

Gia*_*zzi 6

一个执行程序键入: program_name0 arg1 arg2 arg3 ...

所以shell应该已经划分了token,第一个token已经是程序名了。顺便说一句,因此在程序端和外壳上有相同的索引。

我认为这只是一个方便的技巧(在最开始时),并且正如您在其他答案中看到的那样,它也非常方便,因此延续了这一传统并将其设置为 API。


use*_*544 5

基本上, argv 包含程序名称,以便您可以编写类似 的错误消息prgm: file: No such file or directory,这将使用以下内容实现:

    fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );
Run Code Online (Sandbox Code Playgroud)