我怎樣才能給管道計時?
我想要
time
一個由兩個單獨的命令組成的命令,一個管道輸出到另一個。例如,考慮以下兩個腳本:$ cat foo.sh #!/bin/sh sleep 4 $ cat bar.sh #!/bin/sh sleep 2
現在,我怎樣才能
time
報告所花費的時間foo.sh | bar.sh
(是的,我知道管道在這裡沒有意義,但這只是一個例子)?如果我在沒有管道的子shell中按順序執行它們,它確實可以按預期工作:$ time ( foo.sh; bar.sh ) real 0m6.020s user 0m0.010s sys 0m0.003s
但是在管道時我無法讓它工作:
$ time ( foo.sh | bar.sh ) real 0m4.009s user 0m0.007s sys 0m0.003s $ time ( { foo.sh | bar.sh; } ) real 0m4.008s user 0m0.007s sys 0m0.000s $ time sh -c "foo.sh | bar.sh " real 0m4.006s user 0m0.000s sys 0m0.000s
我已經閱讀了一個類似的問題(如何在多個命令上執行時間並將時間輸出寫入文件?)並且還嘗試了獨立的
time
執行檔:$ /usr/bin/time -p sh -c "foo.sh | bar.sh" real 4.01 user 0.00 sys 0.00
如果我創建僅執行管道的第三個腳本,它甚至不起作用:
$ cat baz.sh #!/bin/sh foo.sh | bar.sh
然後時間:
$ time baz.sh real 0m4.009s user 0m0.003s sys 0m0.000s
有趣的是,它並沒有
time
在第一個命令完成後立即退出。如果我bar.sh
改為:#!/bin/sh sleep 2 seq 1 5
再說
time
一次,我希望在time
輸出之前列印輸出,seq
但事實並非如此:$ time ( { foo.sh | bar.sh; } ) 1 2 3 4 5 real 0m4.005s user 0m0.003s sys 0m0.000s
儘管在列印報告1之前等待它完成,但看起來
time
並沒有計算執行時間。bar.sh
所有測試都在 Arch 系統上執行並使用 bash 4.4.12(1)-release。我只能將 bash 用於這個項目的一部分,所以即使
zsh
或其他一些強大的 shell 可以繞過它,這對我來說也不是一個可行的解決方案。那麼,我怎樣才能得到一組管道命令執行的時間呢?而且,當我們這樣做時,為什麼它不起作用?看起來
time
第一個命令完成後立即退出。為什麼?我知道我可以通過以下方式獲得個人時間:
( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time
但我仍然想知道是否可以將整個事情計時為一次操作。
1 這似乎不是緩衝區問題,我嘗試使用 and 執行腳本,
unbuffered
並且stdbuf -i0 -o0 -e0
數字仍然在time
輸出之前列印。
它正在工作。
管道的不同部分同時執行。唯一同步/序列化管道中的程序的是 IO,即一個程序寫入管道中的下一個程序,下一個程序讀取第一個程序寫入的內容。除此之外,它們彼此獨立執行。
由於管道中的程序之間沒有讀取或寫入發生,因此執行管道所需的時間是最長
sleep
呼叫的時間。你還不如寫
time ( foo.sh & bar.sh &; wait )
Terdon在聊天中發布了幾個稍微修改的範例腳本:
#!/bin/sh # This is "foo.sh" echo 1; sleep 1 echo 2; sleep 1 echo 3; sleep 1 echo 4
和
#!/bin/sh # This is "bar.sh" sleep 2 while read line; do echo "LL $line" done sleep 1
查詢是“為什麼
time ( sh foo.sh | sh bar.sh )
返回 4 秒而不是 3+3 = 6 秒?”要查看發生了什麼,包括執行每個命令的大致時間,可以這樣做(輸出包含我的註釋):
$ time ( env PS4='$SECONDS foo: ' sh -x foo.sh | PS4='$SECONDS bar: ' sh -x bar.sh ) 0 bar: sleep 2 0 foo: echo 1 ; The output is buffered 0 foo: sleep 1 1 foo: echo 2 ; The output is buffered 1 foo: sleep 1 2 bar: read line ; "bar" wakes up and reads the two first echoes 2 bar: echo LL 1 LL 1 2 bar: read line 2 bar: echo LL 2 LL 2 2 bar: read line ; "bar" waits for more 2 foo: echo 3 ; "foo" wakes up from its second sleep 2 bar: echo LL 3 LL 3 2 bar: read line 2 foo: sleep 1 3 foo: echo 4 ; "foo" does the last echo and exits 3 bar: echo LL 4 LL 4 3 bar: read line ; "bar" fails to read more 3 bar: sleep 1 ; ... and goes to sleep for one second real 0m4.14s user 0m0.00s sys 0m0.10s
因此,總而言之,由於緩衝了對
echo
in的前兩次呼叫的輸出,管道需要 4 秒,而不是 6 秒foo.sh
。