如何在多个命令上运行时间并将时间输出写入文件?

我想运行time命令来测量多个命令的时间。
我想要做的是:
- 使用time命令来测量运行多个命令所需的时间 - 仅将time输出写入文件 - 将我正在测量的所有命令的stderr写入stderr,而不是写入文件
想做的是:
- 将多个命令写入一个单独的脚本 - 为什么?因为所有这些已经是我通过程序生成的脚本,创建另一个临时脚本会更加混乱。
我目前尝试过的方法是:
  1. /usr/bin/time --output=outtime -p echo "a"; echo "b";
    • 不起作用,只有第一个命令运行了time
  2. /usr/bin/time --output=outtime -p ( echo "a"; echo "b"; )
    • 不起作用,(是意外的标记。
  3. /usr/bin/time --output=outtime -p { echo "a"; echo "b"; }
    • 不起作用,"没有这个文件或目录"。
  4. /usr/bin/time --output=outtime -p ' echo "a"; echo "b";'
    • 不起作用,"没有这个文件或目录"。
  5. time ( echo "a"; echo "b"; ) 2>outtime
    • 不起作用,因为它将所有的STDERR重定向到outtime;我只想要time的输出。
  6. 当然,time --output=outime echo "a";
    • 不起作用,因为--output=outime: 命令未找到
怎么做呢?

1你可以在这里参考另一个答案:https://superuser.com/questions/608591/time-the-execution-time-of-multiple-commands/608596#608596 - BhaveshDiwan
5个回答

使用sh -c 'commands'作为命令,例如:
/usr/bin/time --output=outtime -p sh -c 'echo "a"; echo "b"'

6一个简化版本:time -p sh -c 'echo "a"; echo "b"' - Geo
在命令序列中,是否预计别名无法正常工作? - Muffo
虽然这个答案很好用,但我更喜欢在https://superuser.com/a/608596/135762上分享的版本,因为它使代码更易读,而不仅仅是作为字符串传递给`sh -c`。无论如何,有人能指出一个情况,其中一个版本比另一个更可取吗? - BhaveshDiwan
在那里分享的版本可以运行多个命令,但不会将时间输出保存到文件中,正如此问题所要求的。如果你仔细阅读这个问题,你会发现使用括号创建子shell被提及为不是一个解决方案。 - Jim Paris
1@muffo 你正在生成一个新的shell,所以只有在你生成的新shell中定义了别名,别名才会起作用。在这方面,你可能会发现类似bash -i -c 'echo foo'这样的命令比sh -c 'echo foo'更好用。 - Jim Paris
如果你想要以 h:mm:ss 格式显示经过的时间,请将 -p 替换为 -f %E - Daniel F

不是正确的答案,但与问题非常相关。 获取多个程序的定时统计信息,需要使用括号。如果只有在命令1无错误退出时才运行命令2,则使用分号;&&分隔命令。
time ( command1 ; command2 )

time ( command1 && command2 )

3这很好。更好的是,使用&&在命令之间--像这样time ( command1 && command2 ),这样如果第一个命令失败,就不会继续执行其他命令。 - Bikash Gyawali
2@bikashg这非常取决于您想执行什么操作。在我的情况下,我使用“|”符号,因为我正在三个命令之间进行管道操作。 - Alexis Wilke
非常有帮助。点赞了。在你的回答基础上,这是我的回答。。我展示了一个多行命令。 - Gabriel Staples
请注意,此假设使用类似于ksh、zsh或bash的shell,在shell语法中“time”是一个关键词。它不会调用/usr/bin/time工具。在这种情况下,输出格式以及如何自定义输出格式会因使用的shell而异。只有zsh(以上三种中的一种)具有与GNU time工具类似的功能。 - Stéphane Chazelas

试试这个:

% (time ( { echas z; echo 2 } 2>&3 ) ) 3>&2 2>timeoutput
zsh: command not found: echas
2
% cat timeoutput                                
( { echas z; echo 2; } 2>&3; )  0.00s user 0.00s system 0% cpu 0.004 total

解释:

首先,我们需要找到一种方法来重定向time的输出。由于time是一个shell内置命令,它将整个命令行作为要测量的命令,包括重定向。因此,

% time whatever 2>timeoutput
whatever 2> timeoutput  0.00s user 0.00s system 0% cpu 0.018 total
% cat timeoutput 
zsh: command not found: whatever

[注意:janos的评论意味着对于bash来说情况并非如此。] 我们可以通过在子shell中运行time并将该子shell的输出重定向来实现time输出的重定向。
% (time whatever) 2> timeoutput
% cat timeoutput 
zsh: command not found: whatever
whatever  0.00s user 0.00s system 0% cpu 0.018 total

现在我们已经成功地重定向了time的输出,但它的输出与我们正在测量的命令的错误输出混合在一起。为了区分这两者,我们使用了一个额外的文件描述符。
在"外部",我们有
% (time ... ) 3>&2 2>timeout

这意味着:无论向文件描述符3写入什么内容,都将输出到与文件描述符2(标准错误)相同的位置(终端)。然后我们将标准错误重定向到文件timeout
现在我们有了:所有写入stdout和fd 3的内容将会显示在终端上,而所有写入stderr的内容将会保存到文件中。剩下的就是将被测命令的stderr重定向到fd 3。
% (time whatever 2>&3) 3>&2 2>timeout

现在,要使时间测量超过一个命令,我们需要在(另一个!)子shell中运行它们(用圆括号括起来)。为了将所有命令的错误输出重定向到文件描述符3,我们需要将它们组合在花括号内。
因此,最终我们得到的是:
% (time ( { whatever; ls } 2>&3 ) ) 3>&2 2>timeoutput

就这样。

这是在POSIX shell中的语法错误。可能是一个bashism吗? - josch
@josch 这里使用的是zsh shell。 - angus

这不是对原帖问题的回答。原帖提问者想要将命令的time部分的输出存储到文件中,而不是将所有子命令的stderr输出也存储到文件中。然而,我下面的方法将选择性地将所有输出存储到文件中,包括所有子命令的stderr。这是想要的,而不是原帖提问者想要的。

因此,我的回答只是对问题的标题的回答,即:

如何在多个命令上运行时间并将时间输出写入文件?

如何在bash中计时多行、多子命令命令,并可选择性地将所有输出存储到文件中

例如:如何在bash中使用圆括号(( ))计时多个子命令和整个命令组,并可选择性地将整个输出存储到文件中。

@Martin.T's answer的基础上,我想进一步介绍一下我喜欢使用的带有括号的格式,当我有一个长而复杂的多行命令时。这有点让我想起Python,因为它比大多数我见过的bash更好看。

在每个子命令后面使用time是可选的。在每个子命令后面使用;&&,根据需要选择。

  • ;用于表示“无论如何都运行下一个命令(例如:即使此命令失败)”,
  • &&用于表示:“只有在此命令通过时才运行下一个命令”。
  • 每行后面的反斜杠(\)表示该命令是多行命令,并且继续到下一行。对于多行命令,除了最后一行外,每行都必须有一个反斜杠。

示例通用格式:

# Option 1: only run the command
time ( \
   time command1; \
   time command2 && \
   time command3 && \
   time command4 \
)

要将整个输出存储到名为output.txt的文件中,请执行以下操作。

  1. 额外的一对外部括号也捕获了第一个最外层bash内置的time命令的输出,
  2. 2>&1部分将stderr(文件描述符2)重定向到stdin(文件描述符1),因为否则time会将其结果输出到stderr,并且
  3. 通过使用| tee output.txt将结果导入到tee命令中,这样结果就在stdout上,并且将结果同时显示在屏幕上 并且 写入output.txt文件。
# Option 2: run the command _and_ store the results into an `output.txt` file
(time ( \
   time command1; \
   time command2 && \
   time command3 && \
   time command4 \
)) 2>&1 | tee output.txt

示例演示命令:
# Option 1: only run the command
time ( \
   time sleep 1; \
   time sleep 2 && \
   time sleep 3 && \
   time sleep 1 \
)

# Option 2: run the command _and_ store the results into an `output.txt` file
(time ( \
   time sleep 1; \
   time sleep 2 && \
   time sleep 3 && \
   time sleep 1 \
)) 2>&1 | tee output.txt

示例演示命令和输出:
请注意,您可以看到每个子命令的时间为1秒,2秒,3秒,1秒,然后是总时间7秒:
$ time ( \
>    time sleep 1; \
>    time sleep 2 && \
>    time sleep 3 && \
>    time sleep 1 \
> )

real  0m1.013s
user  0m0.002s
sys   0m0.000s

real  0m2.001s
user  0m0.001s
sys   0m0.000s

real  0m3.001s
user  0m0.001s
sys   0m0.000s

real  0m1.001s
user  0m0.000s
sys   0m0.001s

real  0m7.017s
user  0m0.005s
sys   0m0.001s

如果你运行上面的“选项2”命令,你也会看到这个完全相同的输出已经存储到刚创建的output.txt文件中。用以下命令查看该文件的内容:
cat output.txt

跟进问题

我正在尝试更细致地了解如何以更精细的方式将输出存储到文件中,就像OP想要的那样,但是使用我上面发布的技术。 我已经在这里发布了我的后续问题:如何将嵌套bash命令(括号内)的内部输出存储到一个文件中,将外部输出存储到另一个文件中?


在bash中,这对我来说完美地运行了,不会在控制台上打印任何内容。
> time ( echo "a"; echo "b") 1>/dev/null 2>output_time_file

> cat output_time_file

real    0m0.000s
user    0m0.000s
sys 0m0.000s