我想测量一系列输入变量的程序运行时间(我正在使用 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
会给您两条线,一条线对应管道的每个部分。