为什么 `exec -a` 没有按我预期的方式工作?

为什么 `exec -a` 没有按我预期的方式工作?

我正在阅读exec以及如何使用该-a标志。这SS64 文档看起来准确;他们说:

执行:

执行命令

句法:

exec [-cl] [-a name] [command [arguments]]

选项:

-c   Causes command to be executed with an empty environment.

-l   Place a dash at the beginning of the zeroth arg passed to command.
     (This is what the login program does.)

-a   The shell passes name as the zeroth argument to command.

为了测试这一点,我编写了两个脚本,一个名为 name foo/baz,一个名为 name foo/buzz

#!/usr/bin/env bash
# foo/baz

exec -a blah ./foo/bar 1 2
#!/usr/bin/env bash
# foo/buzz

exec ./foo/bar 1 2

这些脚本中的每一个都运行相同的子脚本 ,foo/bar该子脚本执行以下操作:

#!/usr/bin/env bash

echo "Hello world"
echo "0: $0"
echo "1: $1"
echo "2: $2"

我的目标是看看-a对第 0 个和后续参数有什么影响。如果-a导致第 0 个参数更改为您传递给标志的参数-a,那么当我运行时我期望第 0 个参数foo/bazblah,因为这就是我传递给 的参数-a。但是,当我运行脚本时,两种情况的输出是相同的:

~/Workspace/OpenSource (master)  $ ./foo/baz 
Hello world
0: /Users/richiethomas/Workspace/OpenSource/foo/bar
1: 1
2: 2
~/Workspace/OpenSource (master)  $ ./foo/buzz
Hello world
0: /Users/richiethomas/Workspace/OpenSource/foo/bar
1: 1
2: 2

难道我做错了什么?或者我的期望在某种程度上是不正确的?

另外,一个相关的问题 - 覆盖第 0 个参数的用例是什么,而不是仅通过 $1、$2、$3 等访问任何传入的参数?

答案1

覆盖第 0 个参数的用例是什么......?

有些程序根据它们的调用方式来改变它们的行为。例如,busybox是一个具有这种行为方式的多调用二进制文件。exec -a根据第零个参数的值,我们会得到不同的行为:

$ bash -c 'exec -a date /usr/sbin/busybox'
Sat Sep 17 20:22:14 EDT 2022
$ bash -c 'exec -a uptime /usr/sbin/busybox'
 20:22:17 up 23:48,  load average: 0.10, 0.20, 0.15

这也表明其exec -a <something>行为如记录的那样。

难道我做错了什么?或者我的期望在某种程度上是不正确的?

这里的问题是您正在使用 shell 脚本。当您./foo/baz在命令行上输入时,您实际上并没有运行名为 的命令./foo/baz:您正在运行类似 的命令/bin/bash /path/to/foo/baz。虽然exec -a影响传递给 shell 的第 0 个参数... shell 并不关心,它在设置 shell 脚本可见的变量时使用自己的逻辑,包括$0(其中包含脚本名称),以及位置参数$1$2...(其中包含脚本的参数)。

(这并不是特定于 shell 脚本——这同样适用于几乎任何解释的代码。)

答案2

这是因为您调用的是解释脚本而不是本机二进制文件。编译此 C 程序,将结果放在./foo/bar脚本原来所在的位置:

#include <stdio.h>

int main(int argc, char **argv) {
    puts("Hello world");
    for(int i = 0; i < argc; ++i)
        printf("%d: %s\n", i, argv[i]);
}

一旦你这样做了,你就会看到你所期望的行为。造成差异的原因是,当调用解释脚本时,argv[0]被劫持以告诉解释器脚本的名称。通常这不会被注意到,因为在大多数情况下,无论如何,这就是事实。你之所以注意到它,是因为你不遗余力地让它变得不同。

答案3

关键点是,将为该命令行上的命令exec设置唯一的。$0

尝试一下,例如:

$ bash

$ exec -a SomeName sh -c 'echo "The name is: $0"'
The name is: SomeName

碰巧的是,shell 会关闭,因为 exec 会替换它,当 exec 结束时,shell 进程也会结束。这就是为什么您需要使用命令请求一个新的 shell bash。如果没有,shell 将关闭,并且也会随之关闭终端。

如果您想遵循 exec 命令的作用,请尝试以下操作:

$ strace -fe execve bash -c 'exec -a SomeName echo test' one two
execve("/usr/bin/bash", ["bash", "-c", "exec -a SomeName echo test", "one", "two"], 0x7ffdca326e60 /* 77 vars */) = 0
execve("/usr/bin/echo", ["SomeName", "test"], 0x55d4dcf02c50 /* 76 vars */) = 0
test
+++ exited with 0 +++

当其调用名称发生变化时,这对于进行一些可执行更改非常有用:

$ ( exec -a date -- busybox -ud '12:34' +'%F %T %Z' )
2022-09-18 12:34:00 UTC

$ ( exec -a ls busybox )
fileone
filetwo

相关内容