我怎样才能给管道计时?

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 -e0time输出之前仍然打印数字。

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

  • 我只是说人们可能会惊讶地发现`sleep 10 | sleep 1` 需要一秒钟,而`time sleep 10 | sleep 1` 在 ksh93 中需要 10 秒。在 Bourne shell 中`time sleep 10 | sleep 1` 需要一秒钟,但是您会在 9 秒后突然获得时间输出(仅适用于 `sleep 10` 和来自 `/usr/bin/time`)。 (4认同)
  • 请注意,像 Bourne shell 或 `ksh93` 这样的 shell 只等待管道的最后一个组件(`sleep 3 | sleep 1` 将持续 1 秒)。Bourne shell 没有 `time` 关键字,但是在 `ksh93` 中,当使用 `time` 运行时,所有组件都会等待。 (3认同)

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 时间。