【问题标题】:Set custom argv[0] for shell script为 shell 脚本设置自定义 argv[0]
【发布时间】:2021-10-13 20:35:09
【问题描述】:

如果你有一个以这样的 shebang 开头的脚本

#!/bin/sh

echo argv0 is $0

或者这个

#!/usr/bin/env python3

import sys
print(f"argv0 is {sys.argv[0]}")

然后您使用自定义 argv[0] 运行它,例如像这样(在 Python 中)

subprocess.run(["fake_argv0"], executable="./real_script")

然后由于某种原因它实际上会打印这个:

argv0 is ./real_script

为什么会发生这种情况?有什么办法可以解决这个问题,或者通过更改脚本的运行方式(subprocess.run() 调用)或更改脚本本身中的#! 行?

【问题讨论】:

  • executable 改变了argv0 的值,而不是命令列表。您的第二个 Python 命令相当于 subprocess.run(["./real_script", "fake_argv0"])
  • 据我所知,POSIX shell 不提供为$0 指定替代值的方法。在bash 中,您可以使用-a 参数到exec 来指定不同的名称。我也不知道subprocess 可以改变正在执行的程序的值。
  • 不,不是。第一个参数是要传递给子进程的argvexecutable 指定要运行的实际命令,它只是默认为argv[0]
  • subprocess.run() 确实 更改argv[0] 成功。我用 C 程序对其进行了测试。但是由于某种原因,如果目标是 #! 脚本而不是 ELF 可执行文件,那么它就会出错。
  • @Timmmm POSIX says运行/bin/sh command_file时,“特殊参数0(见特殊参数)应设置为command_file的值”

标签: shell process argv


【解决方案1】:

假设您执行了一个名为 myscript 的脚本:

#!/bin/foo a b

然后像这样执行

./myscript c d

我认为内核会:

  1. argv[0] 替换为您正在执行的脚本的名称。
  2. #! 内容添加到argv

所以你最终得到了。

/bin/foo a b ./myscript c d

即使您使用自定义argv[0] 运行它,内核也会替换它。执行此操作的代码位于 Linux 内核 here

    /*
     * OK, we've parsed out the interpreter name and
     * (optional) argument.
     * Splice in (1) the interpreter's name for argv[0]
     *           (2) (optional) argument to interpreter
     *           (3) filename of shell script (replace argv[0])
     *
     * This is done in reverse order, because of how the
     * user environment and arguments are stored.
     */

对于 Python,这意味着它最终会执行

/usr/bin/python3 ./myscript.py

而 Python 有 some code 可以简单地删除 argv[0] 所以 argv[0] 最终成为 ./myscript.py

【讨论】: