2013-08-20 25 views
2
$ time (exec -a foo echo hello) 
hello 

它好像stderr(其中time將其輸出)泄漏的地方;顯然這不是我想要的。在子shell時間EXEC

我的問題可以用通用術語表述爲「爲什麼在子shell執行另一個程序時寫在終端上的標準錯誤流?」。

的幾個注意事項:

  1. 我需要使用exec-a開關,它改變了過程的第0個參數。我會很感激exec的替代方案來做到這一點,但我不知道任何,現在這種行爲讓我好奇。
  2. 當然,我需要一個子shell,因爲我想讓我的腳本繼續。再次,任何替代方案都會受到歡迎。 exec在一個子shell中甚至是一件好事嗎?
  3. time'一個子外殼一般工作正常,所以它真的必須與exec

難道有人指着我正確的方向嗎?我不確定從哪個參考資料開始,exec描述非常簡潔。

更新:其實,我只是「幸運」time這裏是bash內建。它不分析所有與/usr/bin/time或與任何其他工藝:

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

其實這是有道理的,我們不能通過一個子shell作爲參數。任何想法如何以任何其他方式做到這一點?

更新:總之,我們有四個很好的答案在這裏,所有的不同的,潛在的東西缺乏:

  1. 使用實際的文件系統的鏈接(硬或符號)是bash將默認和time使用一般。積分爲hek2mgl。

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

  2. forktime使用bash和exec使用bash的子shell沒有特殊的語法。

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

  3. forktime使用bash,但exec使用一個微小的包裝。

    time launch -a foo echo hello

  4. forkexec使用bash的特殊語法time。學分sjnarv。

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

認爲該解決方案1對time影響較小的計時器沒有算在「代理」程序exec,但不是很實用(許多文件系統鏈接),技術上也不理想。在所有其他情況下,我們實際上是exec兩次:一次加載代理程序(2和4的子shell,3的包裝)以及一次加載實際程序。這意味着time會計算第二個exec。雖然它可能非常便宜,exec實際上會執行文件系統查找,可能會非常慢(尤其是如果它搜索PATH,或者本身exec*p或代理進程)。

因此,唯一簡潔的方法(就這個問題的答案而言)將修補bash以修改其time關鍵字,以便它可以在將第零個參數設置爲非零值時爲exec。它可能看起來像time -a foo echo hello

回答

1

時間基於wait系統調用。從time手冊頁

時間顯示的大部分信息來自wait3(2)系統調用。

這隻有在time是要執行的命令的父進程時纔有效。但exec創建了一個全新的過程。

隨着時間的需要fork()wait()我不會過多地關注exec(當然有用)的第零個參數。只要創建符號鏈接,然後調用它像:

time link_name > your.file 2>&1 & 
+0

這是有道理的,謝謝。但是,您的建議存在的問題是我無法使用bash exec的'-a'開關。我開始認爲我必須爲此寫一個C封裝器。 – tne

+0

爲什麼不能?你可以使用-a – hek2mgl

+0

那麼是的,但它會改變'時間'的第零個參數,而不是命令的問題(我在這裏只使用'echo'作爲一個例子,顯然),這使得使用' exec'開始。 – tne

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一樣工作,除了因爲它不是內置的,外殼將正常分叉以作爲單獨的進程運行程序。因此time沒有問題。

以下示例輸出中的test程序是一個簡單的程序,它只輸出其參數向量,每行一個參數。

$ ./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編譯器標誌和功能測試宏(海合會/ glibc的):

CFLAGS=-std=c11 -pedantic -Wall -D_XOPEN_SOURCE=700 
+0

我看到你做到了:)我今天有點累了明天會有更近看.. – hek2mgl

1

我不認爲計時器的輸出消失。我認爲它(計時器)在 子shell中運行,由exec覆蓋。

這裏是一個不同的調用。或許,這會產生你所期望的開始:

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

這對我發出:

hello 

real 0m0.002s 
user 0m0.000s 
sys  0m0.001s 
+0

好的趕上!實際上,這裏的'time'不是一個可執行文件,也不是shell內置的 - 它是一個shell *關鍵字*,這意味着它有特殊的語法,在這種情況下,我認爲你是對的,因爲shell運行定時器子外殼(這是有道理的,因爲它是子外殼過程,用於檢索子對象的狀態信息)。我不知道可以用這種方法「複製」複合命令;這具有強制shell來定時子進程(而不是子進程)的效果。謝謝!請注意,因爲這個,'/ usr/bin/time'這個可執行文件不適用於這個方法。 – tne

+0

關於/ usr/bin/time的使用,右鍵 - 在shell的語句中,它在語法上只是一個簡單的命令列表,所以沒有花括號和括號的東西應該可以工作:'/ usr/bin/time bash -c'exec - 一個foo回聲你好' – sjnarv