不要求第一个参数与可执行文件的名称有任何关系:
int main(void)
{
char *args[3] = { "rip van winkle", "30", 0 };
execv("/bin/sleep", args);
return 1;
}
在 Mac 上试一试(经过三个测试):
make x; ./x & sleep 1; ps
第三次运行的输出是:
MiniMac JL: make x; ./x & sleep 1; ps
make: `x' is up to date.
[3] 5557
PID TTY TIME CMD
5532 ttys000 0:00.04 -bash
5549 ttys000 0:00.00 rip van winkle 30
5553 ttys000 0:00.00 rip van winkle 30
5557 ttys000 0:00.00 rip van winkle 30
MiniMac JL:
EBM cmets:
是的,这让它变得更加奇怪。在我的测试 bash 脚本(execve 的目标)中,我看不到 execve 在任何地方的 arg[0] 中的值——不是在环境中,也不是 $0。
修改实验 - 一个名为 'bash.script' 的脚本:
#!/bin/bash
echo "bash script at sleep (0: $0; *: $*)"
sleep 30
还有一个修改后的程序:
int main(void)
{
char *args[3] = { "rip van winkle", "30", 0 };
execv("./bash.script", args);
return 1;
}
这会产生 ps 输出:
bash script at sleep (0: ./bash.script; *: 30)
PID TTY TIME CMD
7804 ttys000 0:00.11 -bash
7829 ttys000 0:00.00 /bin/bash ./bash.script 30
7832 ttys000 0:00.00 sleep 30
在我看来有两种可能:
- 当通过 shebang ('
#!/bin/bash') 行执行脚本时,内核会在命令行中进行调整,或者
- Bash 本身使用它的参数列表。
如何确定差异?我想将 shell 复制到一个替代名称,然后在 shebang 中使用该替代名称会告诉我们一些事情:
$ cp /bin/bash jiminy.cricket
$ sed "s%/bin/bash%$PWD/jiminy.cricket%" bash.script > tmp
$ mv tmp bash.script
$ chmod +w bash.script
$ ./x & sleep 1; ps
[1] 7851
bash script at sleep (0: ./bash.script; *: 30)
PID TTY TIME CMD
7804 ttys000 0:00.12 -bash
7851 ttys000 0:00.01 /Users/jleffler/tmp/soq/jiminy.cricket ./bash.script 30
7854 ttys000 0:00.00 sleep 30
$
我认为这表明内核在使用shebang机制时重写了argv[0]。
解决 nategoose 的评论:
MiniMac JL: pwd
/Users/jleffler/tmp/soq
MiniMac JL: cat al.c
#include <stdio.h>
int main(int argc, char **argv)
{
while (*argv)
puts(*argv++);
return 0;
}
MiniMac JL: make al.c
cc al.c -o al
MiniMac JL: ./al a b 'c d' e
./al
a
b
c d
e
MiniMac JL: cat bash.script
#!/Users/jleffler/tmp/soq/al
echo "bash script at sleep (0: $0; *: $*)"
sleep 30
MiniMac JL: ./x
/Users/jleffler/tmp/soq/al
./bash.script
30
MiniMac JL:
这表明调整argv[0] 值的是shebang '#!/path/to/program' 机制,而不是诸如Bash 之类的任何程序。因此,当执行二进制文件时,argv[0] 的值不会被调整;当通过shebang执行脚本时,参数列表由内核调整; argv[0] 是 shebang 上列出的二进制文件;如果 shebang 之后有参数,则变为argv[1];下一个参数是脚本文件的名称,后跟来自 execv() 或等效调用的任何剩余参数。
MiniMac JL: cat bash.script
#!/Users/jleffler/tmp/soq/al -arg0
#!/bin/bash
#!/Users/jleffler/tmp/soq/jiminy.cricket
echo "bash script at sleep (0: $0; *: $*)"
sleep 30
MiniMac JL: ./x
/Users/jleffler/tmp/soq/al
-arg0
./bash.script
30
MiniMac JL: