在 zsh 中的 time 命令输出中显示变量的值

在 zsh 中的 time 命令输出中显示变量的值

我想测量一系列输入变量的程序运行时间(我正在使用 zsh):

for i in {1..3} ; do time sleep $i ; done

输出是

sleep $i  0,00s user 0,00s system 0% cpu 1,003 total
sleep $i  0,00s user 0,00s system 0% cpu 2,003 total
sleep $i  0,00s user 0,00s system 0% cpu 3,002 total

但我希望它是

sleep 1  0,00s user 0,00s system 0% cpu 1,003 total
sleep 2  0,00s user 0,00s system 0% cpu 2,003 total
sleep 3  0,00s user 0,00s system 0% cpu 3,002 total

如何time打印实际命令,并将所有变量替换为它们的值?


我已经尝试在子 shell 中运行该程序,但它没有按预期更改输出:

for i in {1..3} ; do time (sleep $i) ; done

由于我的程序向 stdout 和 stderr 生成自己的输出,因此我没有找到一种方法来简单地在前面添加所有参数(不仅仅是i),例如:

for i in {1..3} ; do echo -n "i=$i --> " ; time sleep $i ; done

答案1

发生这种情况是因为time是可以作用于复合命令的关键字。 (更准确地说,它作用于管道。)您可以编写类似的内容

time (i=$(($(/bin/echo 1) + 1)); sleep $i)

zsh 解析命令所花费的时间(包括计算 )$i是计时的一部分。

一种解决方案是使用time外部命令。它作用于外部命令,因此 shell 扩展不属于计时的一部分。使用 GNU 时间(默认情况下不打印命令):

% for i in {1..3} ; do =time -f '%C  %Us user %Ss system %P cpu %e total' sleep $i ; done
sleep 1  0.00s user 0.00s system 0% cpu 1.00 total
sleep 2  0.00s user 0.00s system 0% cpu 2.00 total
sleep 3  0.00s user 0.00s system 0% cpu 3.00 total

另一种解决方案是自己打印命令。请注意,下面的代码会在命令输出(如果有)之前打印命令文本。

% for i in {1..3} ; do echo -n "sleep $i"; (TIMEFMT=${TIMEFMT#%J}; time sleep $i); done
sleep 1  0.00s user 0.00s system 0% cpu 1.004 total
sleep 2  0.00s user 0.00s system 0% cpu 2.004 total
sleep 3  0.00s user 0.00s system 0% cpu 3.004 total

另一个解决方案是激活命令跟踪。

% for i in {1..3} ; do (TIMEFMT=${TIMEFMT#%J}; set -x; time sleep $i); done     
+zsh:14> sleep 1
  0.00s user 0.00s system 0% cpu 1.004 total
+zsh:14> sleep 2
  0.00s user 0.00s system 0% cpu 2.004 total
+zsh:14> sleep 3
  0.00s user 0.00s system 0% cpu 3.005 total

另一种解决方案是将所需的文本注入时间格式。

% for i in {1..3} ; do (TIMEFMT="${${:-sleep $i}//\%/%%}${TIMEFMT#%J}"; time sleep $i); done
sleep 1  0.00s user 0.00s system 0% cpu 1.004 total
sleep 2  0.00s user 0.00s system 0% cpu 2.004 total
sleep 3  0.00s user 0.00s system 0% cpu 3.004 total

另一个解决方案是构造尽可能简单的 shell 命令eval。由于这涉及到eval,因此请小心该命令是否可能包含 shell 特殊字符。

% for i in {1..3} ; do eval "time sleep $i"; done                  
sleep 1  0.00s user 0.00s system 0% cpu 1.002 total
sleep 2  0.00s user 0.00s system 0% cpu 2.005 total
sleep 3  0.00s user 0.00s system 0% cpu 3.004 total

答案2

你可以这样做:

$ for i in {1..3} ; do eval "time sleep $i:q"; done
sleep 1  0.00s user 0.00s system 0% cpu 1.004 total
sleep 2  0.00s user 0.00s system 0% cpu 2.003 total
sleep 3  0.00s user 0.00s system 0% cpu 3.002 total

:q在这种情况下,当仅包含数字时, 是不必要的,但在一般情况下,当想要将 的内容作为文字参数$i传递给某些命令时,则需要)。$i

您也可以随时%J用您想要的任何文本替换(它扩展为作业名称1)$TIMEFMT

for i in {1..3}; do
  TIMEFMT="sleep $i: %U user %S system %P cpu %*E total"
  time sleep $i
done

(请记住,如果%要包含在该文本中,则应将其转义为%%


事实上,这并不是该职位的真正名称。例如,您会发现,在time sleep 1 | sleep 2整个管道就是作业的情况下,time会给您两条线,一条线对应管道的每个部分。

相关内容