如何测量程序执行时间并将其存储在变量中

0xC*_*22L 75 bash shell-script time

为了找出 Bash (v4+) 脚本中的某些操作需要多长时间,我想time“单独”解析命令的输出,并(最终)在 Bash 变量 ( let VARNAME=...) 中捕获它。

现在,我正在使用time -f '%e' ...(或者更确切地说command time -f '%e' ...是因为内置了 Bash),但是由于我已经重定向了执行命令的输出,我真的不知道我将如何捕获time命令的输出。基本上这里的问题是到输出分离time从被执行的命令(或多个)的输出。

我想要的是计算启动命令和完成命令之间的时间(以秒为单位)的功能。它不必是time命令或相应的内置命令。


编辑:鉴于以下两个有用的答案,我想添加两个说明。

  1. 我不想丢弃已执行命令的输出,但它最终是在 stdout 还是 stderr 上并不重要。
  2. 我更喜欢直接方法而不是间接方法(即直接捕获输出而不是将其存储在中间文件中)。

date到目前为止使用的解决方案接近我想要的。

bin*_*lse 97

要将 的输出time放入 var 中,请使用以下命令:

usr@srv $ mytime="$(time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$mytime"

real    0m0.006s
user    0m0.001s
sys     0m0.005s
Run Code Online (Sandbox Code Playgroud)

您也可以只要求一个时间类型,例如 utime:

usr@srv $ utime="$( TIMEFORMAT='%lU';time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$utime"
0m0.000s
Run Code Online (Sandbox Code Playgroud)

要获得时间,您也可以使用date +%s.%N,因此在执行前后取它并计算差异:

START=$(date +%s.%N)
command
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
# echo $DIFF
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,日期格式 %N 似乎不适用于 Mac OS X,它只是返回“N”。好的,在 Ubuntu 上。 (8认同)
  • 不过,我不想丢弃命令的输出。所以我猜你的第三个代码块最接近我的想法。尽管我将最后一个写为`DIFF=$((END-START))`,使用算术表达式。:) ... 谢谢你的回答。+1 (4认同)
  • @STATUS_ACCESS_DENIED:Bash 不做浮点运算,所以如果你想要比秒分辨率更好(binfalse 代码中的`.%N`),你要么需要`bc` 或更高级的计算。 (2认同)

Gil*_*il' 19

在 bash 中,time构造的输出转到其标准错误,您可以重定向它影响的管道的标准错误。因此,让我们与写入其输出和错误streamas命令启动:sh -c 'echo out; echo 1>&2 err'。为了不将命令的错误流与 的输出混淆time,我们可以暂时将命令的错误流转移到不同的文件描述符:

{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; }
Run Code Online (Sandbox Code Playgroud)

这将写入outfd 1、errfd 3 和 fd 2:

{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } \
    3> >(sed 's/^/ERR:/') 2> >(sed 's/^/TIME:/') > >(sed 's/^/OUT:/')
Run Code Online (Sandbox Code Playgroud)

err在 fd 2 和 fd 3 上有时间会更愉快,所以我们交换它们,这很麻烦,因为没有直接的方法来交换两个文件描述符:

{ { { time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } 3>&2 2>&4; } 4>&3; } 3> >(sed 's/^/TIME:/') 2> >(sed 's/^/ERR:/') > >(sed 's/^/OUT:/')
Run Code Online (Sandbox Code Playgroud)

这显示了如何对命令的输出进行后处理,但如果要捕获命令的输出及其时间,则需要更加努力。使用临时文件是一种解决方案。事实上,如果您需要捕获命令的标准错误及其标准输出,它是唯一可靠的解决方案。但除此之外,您可以捕获整个输出并利用time具有可预测格式的事实(如果您用于time -p获取POSIX 格式或特定于 bash 的TIMEFORMAT变量)。

nl=$'\n'
output=$(TIMEFORMAT='%R %U %S %P'; mycommand)
set ${output##*$nl}; real_time=$1 user_time=$2 system_time=$3 cpu_percent=$4
output=${output%$nl*}
Run Code Online (Sandbox Code Playgroud)

如果您只关心挂钟时间,那么在date前后运行是一个简单的解决方案(如果由于加载外部命令花费的额外时间而稍微不精确)。


小智 10

如果您在bash(而不是sh)并且您不需要亚秒级精度,您可以date完全跳过调用并执行它而不会产生任何额外的进程,而不必分离组合输出,也不必捕获和解析输出从任何命令:

# SECONDS is a bash special variable that returns the seconds since set.
SECONDS=0
mycmd <infile >outfile 2>errfile
DURATION_IN_SECONDS=$SECONDS
# Now `$DURATION_IN_SECONDS` is the number of seconds.
Run Code Online (Sandbox Code Playgroud)


mar*_*nus 7

随着时间的推移,命令输出在 stdout 上出现,时间在 stderr 上出现。因此,要将它们分开,您可以执行以下操作:

command time -f '%e' [command] 1>[command output file] 2>[time output file]
Run Code Online (Sandbox Code Playgroud)

但是,现在时间在一个文件中。我认为 Bash 不能直接将 stderr 放入变量中。如果您不介意将命令的输出重定向到某处,则可以执行以下操作:

FOO=$((( command time -f '%e' [command]; ) 1>outputfile; ) 2>&1; )
Run Code Online (Sandbox Code Playgroud)

执行此操作时,命令的输出将outputfile$FOO.

  • @STATUS_ACCESS_DENIED:`time` 不会合并其命令的 `stdout` 和 `stderr`。我展示的方式假设您只需要命令的 stdout 输出。由于 `bash` 只会存储来自 `stdout` 的内容,因此您必须重定向。如果您可以安全地删除命令的 stderr:时间将始终位于 stderr 的最后一行。如果您确实需要两个命令的输出流,我建议将其包装在另一个脚本中。 (3认同)

lor*_*isi 5

在将所有以前的回复放在一起时,在 OSX 中

ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G31+
Run Code Online (Sandbox Code Playgroud)

你可以这样做

microtime() {
    python -c 'import time; print time.time()'
}
compute() {
    local START=$(microtime)
    #$1 is command $2 are args
    local END=$(microtime)
    DIFF=$(echo "$END - $START" | bc)
    echo "$1\t$2\t$DIFF"
}
Run Code Online (Sandbox Code Playgroud)