Bash

我怎樣才能給管道計時?

  • January 27, 2020

我想要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

因此,總而言之,由於緩衝了對echoin的前兩次呼叫的輸出,管道需要 4 秒,而不是 6 秒foo.sh

引用自:https://unix.stackexchange.com/questions/364156