2
$ time (exec -a foo echo hello)
hello

stderr(timeその出力を書き込む場所) がどこかに漏れているようです。明らかにこれは私が意図したものではありません。

私の質問は、「サブシェルが別のプログラムを実行するときに、標準エラーストリームが端末に書き込まれないのはなぜですか?」という一般的な言葉で表現できます。

いくつかのメモ:

  1. プロセスのゼロ番目の引数を変更するスイッチに使用execする必要があります。-aこれを行うための代替手段をいただければ幸いexecですが、何も知りません。今、この動作に興味を持ちました。
  2. もちろん、スクリプトを続行したいので、サブシェルが必要です。繰り返しますが、代替案は大歓迎です。execサブシェルで行うことは良いことですか?
  3. time一般に、サブシェルを実行すると問題なく動作するため、実際にはexec.

誰かが私を正しい方向に向けることができますか? 参考資料のどこから始めればよいかわかりませんexec。説明はかなり簡潔です。

更新:time実際、ここに bash が組み込まれているのは 「幸運」でした。/usr/bin/time他のプロセスと一緒に、または他のプロセスとまったく解析しません。

$ env (exec -a foo echo hello)
bash: syntax error near unexpected token `exec'

実際、これは理にかなっています。サブシェルを引数として渡すことはできません。これを他の方法で行う方法はありますか?

更新: 要約すると、ここには 4 つの適切な回答があります。すべてが異なり、何かが欠けている可能性があります。

  1. timebash がデフォルトで通常使用する実際のファイルシステム リンク (ハードまたはシンボリック) を使用します。hek2mgl のクレジット。

    ln $(which echo) foo && time ./foo hello && rm foo

  2. forktimebash を使用execし、特別な構文を使用せずに bash サブシェルを使用する場合。

    time bash -c 'exec -a foo echo hello'

  3. forktimebashをexec使用するが小さなラッパーを使用する場合。

    time launch -a foo echo hello

  4. forkおよび特別な構文で bash を使用する場合exectimesjnarv のクレジット。

    time { (exec -a foo echo hello); }

タイマーが「プロキシ」プログラムでカウントする必要がないため、ソリューション1の影響は少ないと思いますが、あまり実用的ではなく(多くのファイルシステムリンク)、技術的にも理想的ではありません。1 回目はプロキシ プログラム (2 と 4の場合はサブシェル、3 の場合はラッパー) をロードし、もう 1 回は実際のプログラムをロードします。これは、が秒をカウントすることを意味します。非常に安価になる可能性がありますが、実際にはファイルシステムのルックアップを実行しますが、これはかなり遅くなる可能性があります (特に、それ自体を使用して検索する場合、またはプロキシプロセスが検索する場合)。timeexecexectimeexecexecPATHexec*p

したがって、唯一のクリーンな方法 (この質問の回答がカバーする限り) は、bash にパッチを適用してtimeキーワードを変更しexec、0 番目の引数をゼロ以外の値に設定できるようにすることです。おそらく のようになりますtime -a foo echo hello

4

3 に答える 3

1

そのため、私は次のように呼ぶ小さな C ラッパーを作成することになりましたlaunch

#include <stdlib.h>
#include <unistd.h>

int main(const int argc, char *argv[])
{
    int opt;
    char *zeroth = NULL;

    while ((opt = getopt(argc, argv, "a:")) != -1)
        if (opt == 'a')
            zeroth = optarg;
        else
            abort();

    if (optind >= argc) abort();
    argv += optind;
    const char *const program = *argv;
    if (zeroth) *argv = zeroth;
    return execvp(program, argv);
}

本質的なことだけを強調するために、明らかに単純化しました。基本的には と同じようexec -aに機能しますが、組み込みではないため、シェルは通常どおり fork してlaunchプログラムを別のプロセスとして実行します。したがって、問題はありませんtime

次の出力例のtestプログラムは、1 行に 1 つの引数を引数ベクトルとして出力する単純なプログラムです。

$ ./launch ./test hello world
./test
hello
world
$ ./launch -a foo ./test hello world
foo
hello
world
$ time ./launch -a foo ./test hello world
foo
hello
world

real    0m0.004s
user    0m0.001s
sys     0m0.002s
$ ./launch -a foo -- ./test -g hello -t world
foo
-g
hello
-t
world

オーバーヘッドは最小限に抑える必要があります。プログラムをロードし、その単一およびオプションの引数を解析し、引数ベクトルを操作するために必要なものだけです (ほとんどの場合、次のexecvp呼び出しで再利用できます)。

唯一の問題は、(ラップされたプログラムではなく) ラッパーが失敗したことを呼び出し元に通知する良い方法がわからないことです。これは、誤った引数で呼び出された場合に発生する可能性があります。呼び出し元はラップされたプログラムからのステータス コードを期待している可能性が高く、ラッパー用にいくつかのコードを確実に予約する方法がないためabort、少し珍しいほうを使用しますが、適切ではないと感じます (また、そうしません)。すべて問題ありませんが、ラップされたプログラムはまだ中断する可能性があり、呼び出し元が何が問題なのかを診断するのが難しくなります)。しかし、それはおそらくこの質問の範囲にとって興味深いものではありません。

編集:念のため、C コンパイラ フラグと機能テスト マクロ (gcc/glibc):

CFLAGS=-std=c11 -pedantic -Wall -D_XOPEN_SOURCE=700
于 2013-08-20T23:34:28.143 に答える