【问题标题】:How to change argv[0] value using bash/shell? [duplicate]如何使用 bash/shell 更改 argv[0] 值? [复制]
【发布时间】:2018-01-24 02:32:56
【问题描述】:

脚本应该能够更改 shell / bash 脚本中的 argv[0] 值。 我在较旧的帖子上找到了,但无法真正理解它在做什么。 有人可以解释一下这条线是如何工作的吗:

sh -c ". '$0'" argv0new "$@"

test ".$INNERCALL" 也是一个变量吗?

原问题:How to change argv[0] value in shell / bash script?

#! /bin/bash    # try executing this script with several arguments to see the effect

test ".$INNERCALL" = .YES || {
    export INNERCALL=YES
    # this method works both for shell and bash interpreters
    sh -c ". '$0'" argv0new "$@"
    # bash -c ". '$0'" argv0new "$@"
    exit $?
}

printf "argv[0]=$0\n"
i=1 ; for arg in "$@" ; do printf "argv[$i]=$arg\n" ; i=`expr $i + 1` ;done

【问题讨论】:

  • 您已将您的问题标记为bash,但您正在调用sh 并在您的shebang 中使用/bin/sh。你能澄清一下你对什么外壳感兴趣吗?重击? POSIX? (提示:更新您的问题,不要只在 cmets 中回答。)
  • 顺便说一句,sh -c ". '$0'" -- 或 any 使用非常量的 sh -c '...' 值调用 sh -c '...' 的地方 -- 是 非常 b> 从安全角度来看是个坏主意。与sh -c '. "$0" "$@"' argv0new "$@" 进行比较,这是安全的。
  • 顺便说一句,如果你的 shell 是 bash -- 而不是 sh -- 你可以在启动一个新进程时设置argv[0](或者用不同的进程替换 shell就地实例)使用exec-a 参数。
  • 这应该是对original answer to the original question的评论。

标签: bash shell sh


【解决方案1】:

当变量INNERCALL 未设置时,脚本会使用一组特定的参数调用自身。它设置变量以避免无限循环,然后以允许脚本设置自己的$0 的方式调用自身。然后内部实例执行test 之外的代码,这表明$0 现在确实设置为脚本作者选择的特定值。完成后,我们返回脚本的外部实例,然后简单地退出。

真正的问题是sh -c 'script...' arg0 arg1 arg2$0 设置为脚本本身之后的第一个参数(本例中为arg0)。

【讨论】:

  • 如果我要写:firefox -c ". '$0'" argv0new "$@" 在终端中输入 top 时,这是否会更改我正在运行的进程的名称(因为这是脚本的目标),还是只打开 Firefox 并搜索“argv0new”?
  • 那么 Firefox 肯定会抱怨这些是未知选项或者您使用不正确。你可以做的是sh -c 'echo "I am Firefox! See: $0"; sleep 3600' /usr/bin/firefox & 并查看ps 列表。您的 sh 进程将 /usr/bin/firefox 作为其 argv[0] 即明显的进程二进制文件。
【解决方案2】:

让我们逐行看一遍

test ".$INNERCALL" = .YES

基本上这会查看$INNERCALL 中是否已经包含YES 的值。 Shell 的"" 函数以安全的方式进行变量扩展,并将其全部编组为一个值,例如

foo="hello"
bar=", world!"
echo "foobar is: $foo$bar"

打印foobar is: hello, world!


|| {
...
}

这使用|| 运算符表示,如果前面的程序返回一个非零值(例如test 是一个程序,如果相关条件为假则返回 1),则执行此代码块,否则跳过它(有关|| 的更多信息,请参阅this link


export INNERCALL=YES

这会将INNERCALL 设置为YES,这意味着这只对第一级执行


sh -c ". '$0'" argv0new "$@"
exit $?

这就是魔法发生的地方。 sh -c 打开一个新的 shell,然后从后面的字符串中读取它的参数。 ". '$0'" 对当前位于 $0 的值使用 sh 的源函数,这应该是当前文件。

基本上sh -c ". '$0'" 只是在子sh 进程中再次打开当前文件,然后该行的其余部分替换参数: argv0new 成为新的 $0,您还可以通过包含 "$@" 来保留文件的原始参数

然后exit $? 返回子进程运行的任何返回值。

剩下的代码只是为了证明除了$0之外的所有参数都是一样的,$0已经被替换了。


tl;dr 它打开一个子 shell 进程,告诉它读取当前文件,替换参数,然后导出一个测试值,这样它就不会无限循环。

【讨论】:

  • 对它的作用/它如何工作的任何解释都应该涉及安全隐患。 sh -c ". '$0'" 允许控制脚本名称但不控制其内容的攻击者运行任意代码,这可以使通过noexec-mounted /tmp 分区的符号链接攻击有效。使用sh -c '. "$0"' "$0" "$@" 更安全,更多,让内壳进行替换。
  • ...单引号不足以保护,因为文件或目录名称可以包含 literal 单引号;考虑在带有 bash 或 ksh 扩展名的 shell 中运行时使用 ln -s / $'/tmp/$(rm -rf ~)\'$(rm -rf ~)`' 创建的符号链接,让系统上的任何可执行文件都可以使用其名称中的此类值进行访问;如果我们有一个调用此脚本的 setuid 二进制文件...
  • 单引号无论如何都不起作用,'. "$0"' 将与sh -c '. sh' 相同
  • 对于sh -c,作为参数传递给-c 的脚本由子shell 解释,其中$0$1 等取自-c 后面的参数。因此,除非'. "$0"' 后面的参数是sh,否则上述说法是不正确的。
  • ...您可以自己测试一下:sh -c 'echo "0 is $0; 1 is $1"' zero one 发出 0 is zero,而不是 0 is sh
【解决方案3】:

注意它不会改变 argv0 的值,而是使用 argv0new 创建一个新进程

变化

sh -c ". '$0'" argv0new "$@"

通过

exec sh -c ". '$0'" argv0new "$@"

也可以通过 sh 更改 bash。

对于 bash From bash Invoking-Bash

-c

从第一个非选项参数 command_string 读取并执行命令,然后退出。如果 command_string 之后有参数,则将第一个参数分配给 $0,并将任何剩余的参数分配给位置参数。对 $0 的赋值设置了 shell 的名称,用于警告和错误消息。

【讨论】:

  • 对它的作用/它如何工作的任何解释都应该涉及安全隐患。 sh -c ". '$0'" 允许控制脚本名称但不控制其内容的攻击者运行任意代码,这可以使通过noexec-mounted /tmp 分区的符号链接攻击有效。使用sh -c '. "$0"' "$0" "$@" 更安全,很多,因此让内壳进行替换..
  • 如果我试图隐藏 Firefox 正在运行,这个命令会更改它的名称吗? firefox -c ". '$0'" argv0new "$@"
  • @JohnSmith,啊 :),没有 firefox 不是 sh shell,从操作系统的角度来看,这个技巧并没有改变任何命令仍然是 sh 和 arguments -c 命令等,它仅从外壳更改,因此 $0 扩展为 argv0new,因此兴趣非常有限
猜你喜欢
  • 2014-08-20
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-17
相关资源
最近更新 更多