我正在阅读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/baz
是blah
,因为这就是我传递给 的参数-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