在每行前面加上生成它所花费的时间

在每行前面加上生成它所花费的时间

我想要一个脚本,在标准输入的每一行前面加上生成它需要多长时间的信息。基本上用于输入:

foo
bar
baz

我想拥有

0 foo
10 bar
5 baz

其中10是打印foo和打印bar之间经过10秒,与5类似,打印bar之后需要5秒才能打印baz。

我知道有一个实用程序ts可以显示时间戳并且我知道https://github.com/paypal/gnomon,但我不想使用 javascript 来做到这一点。

是否有一个标准工具,或者我应该使用 awk 进行处理?

答案1

我们假设生成输出的脚本称为generate。然后,显示生成每一行所需的秒数:

$ generate | ( t0=$(date +%s); while read -r line; do t1=$(date +%s); echo " $((t1-t0)) $line"; t0=$t1; done )
 2 foo
 0 foo
 5 foo
 3 foo

分布在多行上的相同命令如下所示:

generate | ( t0=$(date +%s)
    while read -r line
    do
        t1=$(date +%s)
        echo " $((t1-t0)) $line"
        t0=$t1
    done
    )

或者,为了方便起见,我们可以定义一个包含以下代码的 shell 函数:

timer() { t0=$(date +%s); while read -r line; do t1=$(date +%s); echo " $((t1-t0)) $line"; t0=$t1; done; }

我们可以如下使用这个函数:

$ generate | timer
 0 foo
 2 foo
 4 foo
 3 foo

怎么运行的

  • t0=$(date +%s)

    这会捕获脚本开始时的当前时间(以秒为单位)。

  • while read -r line; do

    这将启动一个从标准输入读取的循环

  • t1=$(date +%s)

    这捕获了自捕获当前行以来的秒数时间。

  • echo " $((t1-t0)) $line"

    这会打印出当前行所花费的时间(以秒为单位)。

  • t0=$t1

    这将更新t0下一行。

  • done

    这标志着循环的结束while

答案2

trickle () {
    awk 'BEGIN { t = systime(); OFS="\t" }
               { print systime() - t, $0 }
               { t = systime()           }'
}

这个小 shell 函数只是包装了一个awk脚本(与 GNU 和 兼容awk,但由于缺少而mawk -Wi与 BSD 不兼容)。它将输出每行输入,并以自最后一行输出以来的整秒数为前缀。第一行将以零为前缀。awksystime()

测试:

$ { echo hello; sleep 2; echo world } | trickle
0       hello
2       world

awk脚本显然也可以独立运行:

$ some_command | awk 'BEGIN{OFS="\t";t=systime()}{print systime()-t,$0;t=systime()}

例如

$ { echo hello; sleep 2; echo world; } | awk 'BEGIN{OFS="\t";t=systime()}{print systime()-t,$0;t=systime()}'
0       hello
2       world

答案3

出色地,更多实用程序 ts似乎是一个完美的工具:

自开始以来的几秒钟:

$ seq 5 | pv -qL 1 | ts -s %s
2 1
4 2
6 3
8 4
10 5

或者更精确地说:

$ seq 5 | pv -qL 1 | ts -s %.s
1.949228 1
3.933483 2
5.917923 3
7.902231 4
9.886618 5

或者使用zshor ksh93$SECONDS默认情况下是整数,但您可以使用 ; 使其浮动typeset -Fpdksh也可以bash使用$SECONDS,但仅限整数):

$ typeset -F SECONDS=0; seq 5 | pv -qL 1 | while IFS= read -r line; do
   printf '%.3f %s\n' "$SECONDS" "$line"; done
1.987 1
3.972 2
5.956 3
7.941 4
9.925 5

行与行之间的秒数

$ seq 5 | pv -qL 1 | ts -i %.s
1.947814 1
1.984442 2
1.984456 3
1.984151 4
1.984195 5

zsh/ ksh93

$ typeset -F SECONDS; seq 5 | pv -qL 1 | while SECONDS=0; IFS= read -r line; do
  printf '%.6f %s\n' "$SECONDS" "$line"; done
1.985298 1
1.984322 2
1.984212 3
1.984240 4
1.984441 5

答案4

首先,利用 bash 内置函数创建一个简单的函数SECONDS。请注意,我们在每次调用时将其初始化为零。

_time() { SECONDS=0; out="$($@)"; echo "[${SECONDS}] ${out:-no output}"; }

接下来,使用此_time函数从命令列表中运行命令,命令列表可以是命令的文件,或者其他任何内容。

while read cmd; do _time $cmd; done < <(echo -e "sleep 5\nsleep 2\nsleep 1")

在这里, sleep 没有输出,但你的命令可能会有。这只是为了展示 SECONDS 的使用,因为它与这个问题有关。此示例将输出以下默认输出no output

[5] no output
[2] no output
[1] no output

相关内容