如何在 bash 脚本中进行时间测量?

如何在 bash 脚本中进行时间测量?

基于答案,要测量我的 bash 脚本中的时间,我可以使用:

start_time="$(date -u +%s.%N)"
sleep 5
end_time="$(date -u +%s.%N)"

elapsed="$(bc <<<"$end_time-$start_time")"
echo "Total of $elapsed seconds elapsed for process"

而且效果很好。然而,由于我有很多脚本文件,我很想简化和干燥代码。例如我想实现这样的代码:

. /time.sh

start_time=now
sleep 5
end_time=now

measure_time

换句话说,我想要另一个名为time.sh可以包含在任何地方的脚本,并且它应该有两个功能。一种是更换"$(date -u +%s.%N)"零件,一种是更换"$(bc <<<"$end_time-$start_time")"零件。

我尝试创建这些函数,但后来我意识到在 shell 中,我们无法从函数返回值。我们只能返回数字。

有没有办法将上面的代码简化为下面的代码?

答案1

出什么问题了time ./script.sh

简单的例子:-

睡觉时间5

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

答案2

在最新版本的 bash (5.0+) 中,有一个变量可以以微秒(6 个小数位)的精度给出时间。如果这对于您的使用来说足够精确,您可以尝试以下脚本:

#!/bin/bash --
time_diff(){ 
    local elapsed="$(bc <<<"${send}-${start}")"
    echo "Total of $elapsed seconds elapsed for process";
 }

start=$EPOCHREALTIME
sleep 5
end=$EPOCHREALTIME

time_diff

该脚本获取时间值的速度比调用外部可执行文件 ( date) 执行相同操作的速度更快。

答案3

从最简单的形式来看,这听起来就像您所追求的。

now(){
  date -u +%s.%N
}

measure_time(){
  local elapsed="$(bc <<<"$end_time-$start_time")"
  echo "Total of $elapsed seconds elapsed for process"
}

start_time="$(now)"
sleep 5
end_time="$(now)"

measure_time

您可以使该now函数接受一个命名变量并调整该measure_time函数来处理该变量,但如果您的用例是一次跟踪一个进程,那么这将是一种不必要的复杂化。

话虽如此,这是朝这个方向的迭代。 startend是任意标签,因此如果您要跟踪多个事件,您可以将它们称为任何合适的名称。

stamp(){
  time[$1]="$(date -u +%s.%N)"
}

measure_time(){
  local elapsed="$(bc <<<"${time[$2]}-${time[$1]}")"
  echo "Total of $elapsed seconds elapsed for process"
}

declare -A time=()

stamp start
sleep 5
stamp end

measure_time start end

编辑:

警告

正如 roaima 的答案中的数字所示,这不会是高精度,因为每个操作(特别是子 shell 和外部进程)都会将报告的超时填充几分之一秒。这对于多分钟的过程可能并不重要(在这种情况下为什么要测量纳秒?),但在较低的运行时间下会变得更加明显,甚至几乎毫无意义。尽管在我的测试中,与 roaima 的测试相比,问题并没有那么严重。这更取决于您使用该数据的目的。

另一个编辑:

为了回应 QuartzCrystal 的公正评论,这里有一个进一步的迭代,事实证明,它正在收敛于 roaima 的答案——可能表明他们处于“最佳”轨道上。

_stamp(){
  time[${1:?}]="$(date -u +%s.%N)"
}

_start() {
  _stamp "start${1:+_${1}}"
}

_end() {
  _stamp "end${1:+_${1}}"
}

measure_time(){
  local elapsed="$(bc <<<"${time[end${1:+_${1}}]}-${time[start${1:+_${1}}]}")"
  echo "Total of $elapsed seconds elapsed${1:+ for process ${1}}"
}

declare -A time=()

_start
sleep 5
_end

measure_time

_start foo
sleep 1
_start bar
sleep 2
_end foo
_end bar

measure_time foo
measure_time bar

答案4

这是一个需要关联数组的版本(bash等)

放在一个例子中timers.sh

#!/bin/bash
########################################################################
declare -A _timers

# Declare and start a timer. Optional name
startTimer() {
    _timers[${1:-_}_s]=$(date -u +'%s.%N')
}

# End a timer. Optional name
endTimer() {
    _timers[${1:-_}_e]=$(date -u +'%s.%N')
}

# Report on the duration of a timer. Optional name
diffTimer() {
    local s=${_timers[${1:-_}_s]} e=${_timers[${1:-_}_e]}
    bc <<< "${e:-$(date -u +'%s.%N')} - ${s:-0}"
}

主要代码

#!/bin/bash
. timers.sh

startTimer
sleep 2
startTimer sub
sleep 0.1
endTimer sub
endTimer

echo "Elapsed $(diffTimer) and for sub $(diffTimer sub)"

输出

Elapsed 2.321831200 and for sub .183521000

如果没有参数,函数将作用于默认计时器。否则,它们将按照指定的计时器进行操作。如果不调用startTimerendTimer使用默认值0现在被使用。定时器存储在全局关联数组中_timers[]

相关内容