使用exec -a修改shell脚本的$0/argv[0]失效原因排查
为什么
exec -a设置的argv[0]在shell脚本中会被重置? 我尝试替换程序的$0/argv[0]:
- 编译以下C代码:
#include <stdio.h> int main(int argc, char** argv) { printf("# %s #\n", argv[0]); }
执行exec -a fake ./run后,输出为:
# fake #
- 接着使用以下shell脚本:
#!/bin/bash echo "# $0 #"
但执行exec -a fake ./test.sh时,输出却是:
# /home/user/test.sh #
为什么在shell脚本场景中参数0被重置?以下是exec --help的中文翻译内容:
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...] 用指定命令替换当前shell。 执行COMMAND,用指定程序替换当前shell。ARGUMENTS会成为COMMAND的参数。如果未指定COMMAND, 任何重定向操作会在当前shell中生效。 选项: -a name 将NAME作为COMMAND的第0个参数传入 -c 在空环境中执行COMMAND -l 在COMMAND的第0个参数前加一个短横线
原因解析
核心问题出在shell脚本的解释器启动机制上:
当你执行exec -a fake ./test.sh时,exec确实把fake作为./test.sh的argv[0]传递了,但脚本本身不是可执行二进制程序——内核读取到脚本开头的#!/bin/bash(shebang行)后,会启动/bin/bash进程,把脚本的完整路径(/home/user/test.sh)作为bash自身的argv[0],而你通过exec -a设置的fake会被当作额外参数(也就是bash的argv[1])传递进去。
对于bash来说,当它以“执行脚本”的模式启动时,会自动将自身的argv[0]设置为脚本的路径,这就覆盖了你之前通过exec -a设置的值。你可以在脚本里加一行echo "argv1: $1"验证,执行后会输出argv1: fake,说明这个值并没有丢失,只是没被当作$0。
对比两种场景:
- 编译后的二进制程序:
exec -a直接设置它的argv[0],程序启动后会直接读取并使用这个值。 - shell脚本:
exec -a设置的是脚本文件这个“参数”的argv[0],但内核会启动解释器bash,bash会重置自身的argv[0]为脚本路径。
解决方法
如果想要让shell脚本的$0变成fake,需要直接把exec -a指向bash解释器,同时指定脚本:
exec -a fake /bin/bash ./test.sh
执行这个命令后,脚本里的$0就会输出fake了。
内容的提问来源于stack exchange,提问作者Baruch
相关产品推荐
相关产品推荐

