我正在用bash编写一个脚本来计算执行命令所花费的时间,考虑如下:
STARTTIME=$(date +%s)
#command block that takes time to complete...
#........
ENDTIME=$(date +%s)
echo "It takes $($ENDTIME - $STARTTIME) seconds to complete this task..."
我想我的逻辑是正确的,但我最终打印出以下内容:
"It takes seconds to complete this task…"
我的字符串计算有什么问题吗?
我相信bash变量是无类型的,我希望bash中有一个"字符串到整数"的方法。
我发现使用内部变量"$SECONDS"非常简洁
SECONDS=0 ; sleep 10 ; echo $SECONDS
$(())
或$[]
都可以用于计算算术运算的结果。您正在使用$()
,它只是简单地将字符串作为命令进行评估。这是一个微妙的区别。
正如tink在这个回答的评论中指出的那样,$[]
是不赞成的,$(())
应该被青睐。
您正在尝试将ENDTIME
中的数字作为命令执行。您还应该看到类似 1370306857: command not found
的错误。而是使用算术展开:
echo "It takes $((ENDTIME - STARTTIME)) seconds to complete this task..."
您也可以将这些命令保存在单独的脚本commands.sh
中,并使用time命令:
time commands.sh
您可以在这里使用Bash的time
关键字和适当的格式字符串
TIMEFORMAT='It takes %R seconds to complete this task...'
time {
#command block that takes time to complete...
#........
}
以下是参考文献对TIMEFORMAT
的描述:
该参数的值用作格式字符串,指定以
time
为前缀的管道的定时信息如何应显示保留字。'%
'字符引入了一个扩展为时间值或其他信息的转义序列。转义序列及其含义如下:的括号表示可选的部分。%% A literal ‘%’. %[p][l]R The elapsed time in seconds. %[p][l]U The number of CPU seconds spent in user mode. %[p][l]S The number of CPU seconds spent in system mode. %P The CPU percentage, computed as (%U + %S) / %R.
可选的p是指定精度的数字,即小数点后的小数位数。0表示no要输出的小数点或分数。在最多三个地方之后可以指定小数点;p大于3的值被修改为3。如果未指定p,则使用值3。
可选的
l
指定较长的格式(包括分钟),格式为MMmSS.FFs。p的值决定是否如果没有设置这个变量,Bash就会认为它具有
值$'nrealt%3lRnusert%3lUnsyst%3lS'
如果该值为null,则不显示定时信息。当显示格式字符串时,将添加尾随换行符。
对于较大的数字,我们可能希望以更易读的格式打印。下面的示例与其他示例相同,但也以"human"格式打印:
secs_to_human() {
if [[ -z ${1} || ${1} -lt 60 ]] ;then
min=0 ; secs="${1}"
else
time_mins=$(echo "scale=2; ${1}/60" | bc)
min=$(echo ${time_mins} | cut -d'.' -f1)
secs="0.$(echo ${time_mins} | cut -d'.' -f2)"
secs=$(echo ${secs}*60|bc|awk '{print int($1+0.5)}')
fi
echo "Time Elapsed : ${min} minutes and ${secs} seconds."
}
简单测试:secs_to_human "300"
secs_to_human "305"
secs_to_human "59"
secs_to_human "60"
secs_to_human "660"
secs_to_human "3000"
输出:Time Elapsed : 5 minutes and 0 seconds.
Time Elapsed : 5 minutes and 5 seconds.
Time Elapsed : 0 minutes and 59 seconds.
Time Elapsed : 1 minutes and 0 seconds.
Time Elapsed : 11 minutes and 0 seconds.
Time Elapsed : 50 minutes and 0 seconds.
在其他文章中描述的脚本中使用(捕获起始点,然后调用具有完成时间的函数:
)start=$(date +%s)
# << performs some task here >>
secs_to_human "$(($(date +%s) - ${start}))"
尝试以下代码:
start=$(date +'%s') && sleep 5 && echo "It took $(($(date +'%s') - $start)) seconds"
这是Mike Q函数的一个单行替代方案:
secs_to_human() {
echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s"
}
尝试使用时间和经过的秒数选项:
/usr/bin/time -f%e sleep 1
under bash.
或time -f%e sleep 1
在交互式bash。
查看时间手册页:
bash shell的用户需要使用显式路径才能运行外部时间命令,而不是shell内置的变体。对系统time安装在/usr/bin中,第一个示例将变成/usr/bin/time wc/etc/hosts
和
FORMATTING THE OUTPUT
...
% A literal '%'.
e Elapsed real (wall clock) time used by the process, in
seconds.
组合内部变量"$SECONDS"在lonkaut的回答中,ssc的一行字在内部服务器错误的回答下:
SECONDS=0
sleep 2s
echo "Elapsed time: $((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
($/${}对于算术变量是不必要的)
这是一个老帖子,似乎每个人都喜欢在这里写太多代码。但实际上,您只需要3行简单的bash代码就可以以格式良好的方式正确显示运行时间。
START_TIME=$(date +%s)
# put your code here
ELAPSED=$(($(date +%s) - START_TIME))
printf "elapsed: %snn" "$(date -d@$ELAPSED -u +%H hours %M min %S sec)"
这会产生以下结果:
elapsed: 0 hours 2 min 19 sec
测试代码
START_TIME=$(date +%s)
ELAPSED=86399
printf "elapsed: %snn" "$(date -d@$ELAPSED -u +%H hour %M min %S sec)"
elapsed: 23 hour 59 min 59 sec
如果你也想显示日期,那么你需要做一个小技巧,以便正确显示日期:
ELAPSED=86399
printf "elapsed: %snn" "$(date -d@$ELAPSED -u +%d days %H hour %M min %S sec)"
elapsed: 01 days 23 hour 59 min 59 sec
ELAPSED=86400
elapsed: 02 days 00 hour 00 min 00 sec
可以看到,日期的计算方式是错误的,因此需要在bash中执行以下数学操作:
printf "elapsed: %s day %snn" "$(($(date -d@$ELAPSED -u +%d)-1))" "$(date -d@$ELAPSED -u +%H hour %M min %S sec)"
elapsed: 1 day 00 hour 00 min 00 sec
日期-时间模式中的字符转义空格。
start=$(date +%Y%m%d%H%M%S);
for x in {1..5};
do echo $x;
sleep 1; done;
end=$(date +%Y%m%d%H%M%S);
elapsed=$(($end-$start));
ftime=$(for((i=1;i<=$((${#end}-${#elapsed}));i++));
do echo -n "-";
done;
echo ${elapsed});
echo -e "Start : ${start}nStop : ${end}nElapsed: ${ftime}"
Start : 20171108005304
Stop : 20171108005310
Elapsed: -------------6
#!/bin/bash
time_elapsed(){
appstop=$1; appstart=$2
ss_strt=${appstart:12:2} ;ss_stop=${appstop:12:2}
mm_strt=${appstart:10:2} ;mm_stop=${appstop:10:2}
hh_strt=${appstart:8:2} ; hh_stop=${appstop:8:2}
dd_strt=${appstart:6:2} ; dd_stop=${appstop:6:2}
mh_strt=${appstart:4:2} ; mh_stop=${appstop:4:2}
yy_strt=${appstart:0:4} ; yy_stop=${appstop:0:4}
if [ "${ss_stop}" -lt "${ss_strt}" ]; then ss_stop=$((ss_stop+60)); mm_stop=$((mm_stop-1)); fi
if [ "${mm_stop}" -lt "0" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
if [ "${mm_stop}" -lt "${mm_strt}" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
if [ "${hh_stop}" -lt "0" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi
if [ "${hh_stop}" -lt "${hh_strt}" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi
if [ "${dd_stop}" -lt "0" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi
if [ "${dd_stop}" -lt "${dd_strt}" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi
if [ "${mh_stop}" -lt "0" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi
if [ "${mh_stop}" -lt "${mh_strt}" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi
ss_espd=$((10#${ss_stop}-10#${ss_strt})); if [ "${#ss_espd}" -le "1" ]; then ss_espd=$(for((i=1;i<=$((${#ss_stop}-${#ss_espd}));i++)); do echo -n "0"; done; echo ${ss_espd}); fi
mm_espd=$((10#${mm_stop}-10#${mm_strt})); if [ "${#mm_espd}" -le "1" ]; then mm_espd=$(for((i=1;i<=$((${#mm_stop}-${#mm_espd}));i++)); do echo -n "0"; done; echo ${mm_espd}); fi
hh_espd=$((10#${hh_stop}-10#${hh_strt})); if [ "${#hh_espd}" -le "1" ]; then hh_espd=$(for((i=1;i<=$((${#hh_stop}-${#hh_espd}));i++)); do echo -n "0"; done; echo ${hh_espd}); fi
dd_espd=$((10#${dd_stop}-10#${dd_strt})); if [ "${#dd_espd}" -le "1" ]; then dd_espd=$(for((i=1;i<=$((${#dd_stop}-${#dd_espd}));i++)); do echo -n "0"; done; echo ${dd_espd}); fi
mh_espd=$((10#${mh_stop}-10#${mh_strt})); if [ "${#mh_espd}" -le "1" ]; then mh_espd=$(for((i=1;i<=$((${#mh_stop}-${#mh_espd}));i++)); do echo -n "0"; done; echo ${mh_espd}); fi
yy_espd=$((10#${yy_stop}-10#${yy_strt})); if [ "${#yy_espd}" -le "1" ]; then yy_espd=$(for((i=1;i<=$((${#yy_stop}-${#yy_espd}));i++)); do echo -n "0"; done; echo ${yy_espd}); fi
echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}"
#return $(echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}")
}
mh_days(){
mh_stop=$1; yy_stop=$2; #also checks if it's leap year or not
case $mh_stop in
[1,3,5,7,8,10,12]) mh_stop=31
;;
2) (( !(yy_stop % 4) && (yy_stop % 100 || !(yy_stop % 400) ) )) && mh_stop=29 || mh_stop=28
;;
[4,6,9,11]) mh_stop=30
;;
esac
return ${mh_stop}
}
appstart=$(date +%Y%m%d%H%M%S); read -p "Wait some time, then press nay-key..." key; appstop=$(date +%Y%m%d%H%M%S); elapsed=$(time_elapsed $appstop $appstart); echo -e "Start...: ${appstart:0:4}-${appstart:4:2}-${appstart:6:2} ${appstart:8:2}:${appstart:10:2}:${appstart:12:2}nStop....: ${appstop:0:4}-${appstop:4:2}-${appstop:6:2} ${appstop:8:2}:${appstop:10:2}:${appstop:12:2}n$(printf '%0.1s' "="{1..30})nElapsed.: ${elapsed}"
exit 0
-------------------------------------------- return
Wait some time, then press nay-key...
Start...: 2017-11-09 03:22:17
Stop....: 2017-11-09 03:22:18
==============================
Elapsed.: 0000-00-00 00:00:01