なぜ `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.

これをテストするために2つのスクリプトを作成しました。 1つはname foo/baz、もう1つは次のとおりです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番目以降のパラメータにどのような影響があるかを確認することです。-a0番目のパラメータがフラグに渡されたパラメータに変更されると、-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

私は何が間違っていましたか?それとも私の期待は何か間違っているのでしょうか?

また、関連する質問です。 $ 1、$ 2、$ 3などを介して着信パラメータにアクセスするのではなく、0番目のパラメータをオーバーライドするユースケースは何ですか?

答え1

0番目のパラメータをオーバーライドするユースケースは何ですか?

一部のプログラムは、呼び出し方法に応じて動作を変更します。例えば、busyboxこのように動作するマルチコールバイナリです。exec -a0番目のパラメータの値によって異なる動作が表示されます。

$ 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>文書化されたとおりに機能することも示しています。

私は何が間違っていましたか?それとも私の期待は何か間違っているのでしょうか?

ここでの問題は、シェルスクリプトを使用していることです。./foo/bazコマンドラインに入力するときは、実際にはというコマンドを実行するわけではありません./foo/baz。のようなコマンドを実行することです/bin/bash /path/to/foo/baz。シェルに渡される0番目の引数には影響しますが...シェルは気にせず、次のようなシェルexec -aスクリプトに表示される変数を設定するときに独自のロジックを使用します。$0スクリプトname)と位置パラメータ$1$2...(スクリプトのパラメータを含む)。

(これはシェルスクリプトに限定されていません。ほとんどすべての解釈されたコードに同じことが当てはまります。)

答え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

偶然にもexecがシェルを置き換えるので、シェルは閉じられ、execが終わったらシェルプロセスも同じです。そのため、コマンドを使用して新しいシェルを要求する必要がありますbash。それ以外の場合は、シェルが閉じ、端末も閉じます。

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

関連情報