ter*_*don 32 shell bash pipe time
我想要time一个命令,它由两个单独的命令组成,一个管道输出到另一个。例如,考虑以下两个脚本:
$ cat foo.sh
#!/bin/sh
sleep 4
$ cat bar.sh
#!/bin/sh
sleep 2
Run Code Online (Sandbox Code Playgroud)
现在,我怎样才能time报告所花费的时间foo.sh | bar.sh(是的,我知道管道在这里没有意义,但这只是一个例子)?如果我在没有管道的子shell中按顺序运行它们,它确实按预期工作:
$ time ( foo.sh; bar.sh )
real 0m6.020s
user 0m0.010s
sys 0m0.003s
Run Code Online (Sandbox Code Playgroud)
但是我在管道时无法让它工作:
$ 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
Run Code Online (Sandbox Code Playgroud)
我已经阅读了一个类似的问题(How to run time on multiple commands AND write the time output to file?)并尝试了独立的time可执行文件:
$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00
Run Code Online (Sandbox Code Playgroud)
如果我创建仅运行管道的第三个脚本,它甚至不起作用:
$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh
Run Code Online (Sandbox Code Playgroud)
然后时间:
$ time baz.sh
real 0m4.009s
user 0m0.003s
sys 0m0.000s
Run Code Online (Sandbox Code Playgroud)
有趣的是,time第一个命令完成后似乎不会立即退出。如果我bar.sh改为:
#!/bin/sh
sleep 2
seq 1 5
Run Code Online (Sandbox Code Playgroud)
然后time再次,我期待在time之前进行打印输出seq,但它不是:
$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5
real 0m4.005s
user 0m0.003s
sys 0m0.000s
Run Code Online (Sandbox Code Playgroud)
尽管在打印报告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
Run Code Online (Sandbox Code Playgroud)
但我仍然想知道是否有可能将整个过程作为单个操作进行计时。
1 这似乎不是缓冲区问题,我尝试使用unbuffered和运行脚本,并且stdbuf -i0 -o0 -e0在time输出之前仍然打印数字。
Kus*_*nda 39
它正在工作。
管道的不同部分是并发执行的。唯一同步/序列化管道中的进程的是 IO,即一个进程写入管道中的下一个进程,下一个进程读取第一个进程写入的内容。除此之外,它们彼此独立执行。
由于管道中的进程之间没有读取或写入发生,因此执行管道所需的时间是最长sleep调用的时间。
你还不如写
time ( foo.sh & bar.sh &; wait )
Run Code Online (Sandbox Code Playgroud)
Terdon在聊天中发布了几个稍微修改过的示例脚本:
#!/bin/sh
# This is "foo.sh"
echo 1; sleep 1
echo 2; sleep 1
echo 3; sleep 1
echo 4
Run Code Online (Sandbox Code Playgroud)
和
#!/bin/sh
# This is "bar.sh"
sleep 2
while read line; do
echo "LL $line"
done
sleep 1
Run Code Online (Sandbox Code Playgroud)
查询是“为什么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
Run Code Online (Sandbox Code Playgroud)
因此,总而言之,由于前两次调用echoin的输出缓冲,管道需要 4 秒,而不是 6 秒foo.sh。
ilk*_*chu 11
这会是一个更好的例子吗?
$ time perl -e 'alarm(3); 1 while 1;' | perl -e 'alarm(4); 1 while 1;'
Alarm clock
real 0m4.004s
user 0m6.992s
sys 0m0.004s
Run Code Online (Sandbox Code Playgroud)
脚本busyloop 3 和4 秒(分别),由于并行执行总共需要4 秒的实时时间,以及7 秒的CPU 时间。(至少大约。)
或这个:
$ time ( sleep 2; echo) | ( read x; sleep 3 )
real 0m5.004s
user 0m0.000s
sys 0m0.000s
Run Code Online (Sandbox Code Playgroud)
它们不是并行运行的,因此总时间为 5 秒。这一切都花在睡眠上,所以没有使用 CPU 时间。