bash 如何计算bash脚本中经过的时间?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8903239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to calculate time elapsed in bash script?
提问by Misha Moroshko
I print the start and end time using date +"%T"
, which results in something like:
我使用 打印开始和结束时间date +"%T"
,结果如下:
10:33:56
10:36:10
How could I calculate and print the difference between these two?
我如何计算和打印这两者之间的差异?
I would like to get something like:
我想得到类似的东西:
2m 14s
回答by Daniel Kamil Kozar
Bash has a handy SECONDS
builtin variable that tracks the number of seconds that have passed since the shell was started. This variable retains its properties when assigned to, and the value returned after the assignment is the number of seconds since the assignment plus the assigned value.
Bash 有一个方便的SECONDS
内置变量,用于跟踪自 shell 启动以来经过的秒数。该变量在赋值时保留其属性,赋值后返回的值是赋值后的秒数加上赋值的秒数。
Thus, you can just set SECONDS
to 0 before starting the timed event, simply read SECONDS
after the event, and do the time arithmetic before displaying.
因此,您可以SECONDS
在开始计时事件之前设置为 0,只需SECONDS
在事件之后读取,并在显示之前进行时间运算。
SECONDS=0
# do some work
duration=$SECONDS
echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."
As this solution doesn't depend on date +%s
(which is a GNU extension), it's portable to all systems supported by Bash.
由于此解决方案不依赖date +%s
(它是 GNU 扩展),因此它可以移植到 Bash 支持的所有系统。
回答by Misha Moroshko
Seconds
秒
To measure elapsed time (in seconds) we need:
要测量经过的时间(以秒为单位),我们需要:
- an integer that represents the count of elapsed seconds and
- a way to convert such integer to an usable format.
- 一个整数,表示经过的秒数和
- 一种将此类整数转换为可用格式的方法。
An integer value of elapsed seconds:
已用秒的整数值:
There are two bash internal ways to find an integer value for the number of elapsed seconds:
Bash variable SECONDS (if SECONDS is unset it loses its special property).
Setting the value of SECONDS to 0:
SECONDS=0 sleep 1 # Process to execute elapsedseconds=$SECONDS
Storing the value of the variable
SECONDS
at the start:a=$SECONDS sleep 1 # Process to execute elapsedseconds=$(( SECONDS - a ))
Bash printf option
%(datefmt)T
:a="$(TZ=UTC0 printf '%(%s)T\n' '-1')" ### `-1` is the current time sleep 1 ### Process to execute elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - a ))
有两种 bash 内部方法可以找到经过秒数的整数值:
Bash 变量 SECONDS(如果 SECONDS 未设置,它将失去其特殊属性)。
将 SECONDS 的值设置为 0:
SECONDS=0 sleep 1 # Process to execute elapsedseconds=$SECONDS
SECONDS
在开始时存储变量的值:a=$SECONDS sleep 1 # Process to execute elapsedseconds=$(( SECONDS - a ))
Bash printf 选项
%(datefmt)T
:a="$(TZ=UTC0 printf '%(%s)T\n' '-1')" ### `-1` is the current time sleep 1 ### Process to execute elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - a ))
Convert such integer to an usable format
将此类整数转换为可用格式
The bash internal printf
can do that directly:
bash 内部printf
可以直接做到这一点:
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45
similarly
相似地
$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34
butthis will fail for durations of more than 24 hours, as we actually print a wallclock time, not really a duration:
但这会在超过 24 小时的持续时间内失败,因为我们实际上打印的是挂钟时间,而不是真正的持续时间:
$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
06:12:24
For the lovers of detail, from bash-hackers.org:
对于细节爱好者,来自bash-hackers.org:
%(FORMAT)T
outputs the date-time string resulting from using FORMAT as a format string forstrftime(3)
. The associated argument is the number of seconds since Epoch, or -1 (current time) or -2 (shell startup time). If no corresponding argument is supplied, the current time is used as default.
%(FORMAT)T
输出使用 FORMAT 作为格式字符串产生的日期时间字符串strftime(3)
。关联的参数是自Epoch以来的秒数,或 -1(当前时间)或 -2(shell 启动时间)。如果没有提供相应的参数,则默认使用当前时间。
So you may want to just call textifyDuration $elpasedseconds
where textifyDuration
is yet another implementation of duration printing:
因此,您可能只想调用textifyDuration $elpasedseconds
wheretextifyDuration
是持续时间打印的另一个实现:
textifyDuration() {
local duration=
local shiff=$duration
local secs=$((shiff % 60)); shiff=$((shiff / 60));
local mins=$((shiff % 60)); shiff=$((shiff / 60));
local hours=$shiff
local splur; if [ $secs -eq 1 ]; then splur=''; else splur='s'; fi
local mplur; if [ $mins -eq 1 ]; then mplur=''; else mplur='s'; fi
local hplur; if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi
if [[ $hours -gt 0 ]]; then
txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
elif [[ $mins -gt 0 ]]; then
txt="$mins minute$mplur, $secs second$splur"
else
txt="$secs second$splur"
fi
echo "$txt (from $duration seconds)"
}
GNU date.
GNU 日期。
To get formated time we should use an external tool (GNU date) in several ways to get up to almost a year length and including Nanoseconds.
为了获得格式化的时间,我们应该以多种方式使用外部工具(GNU 日期)来获得几乎一年的长度,包括纳秒。
Math inside date.
数学里面的日期。
There is no need for external arithmetic, do it all in one step inside date
:
不需要外部算术,在里面一步完成date
:
date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"
Yes, there is a 0
zero in the command string. It is needed.
是的,0
命令字符串中有一个零。这是必需的。
That's assuming you could change the date +"%T"
command to a date +"%s"
command so the values will be stored (printed) in seconds.
假设您可以将date +"%T"
命令更改为命令,date +"%s"
以便在几秒钟内存储(打印)值。
Note that the command is limited to:
请注意,该命令仅限于:
- Positive values of
$StartDate
and$FinalDate
seconds. - The value in
$FinalDate
is bigger (later in time) than$StartDate
. - Time difference smaller than 24 hours.
- You accept an output format with Hours, Minutes and Seconds. Very easy to change.
- It is acceptable to use -u UTC times. To avoid "DST" and local time corrections.
$StartDate
和$FinalDate
秒的正值。- in 的值
$FinalDate
大于(在时间上)比$StartDate
. - 时差小于24小时。
- 您接受带有小时、分钟和秒的输出格式。非常容易改变。
- 使用 -u UTC 时间是可以接受的。避免“DST”和本地时间更正。
If you mustuse the 10:33:56
string, well, just convert it to seconds,
also, the word seconds could be abbreviated as sec:
如果您必须使用10:33:56
字符串,那么,只需将其转换为秒,
同样,单词 seconds 可以缩写为 sec:
string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"
Note that the seconds time conversion (as presented above) is relative to the start of "this" day (Today).
请注意,秒时间转换(如上所示)是相对于“这一天”(今天)的开始。
The concept could be extended to nanoseconds, like this:
这个概念可以扩展到纳秒,如下所示:
string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"
If is required to calculate longer (up to 364 days) time differences, we must use the start of (some) year as reference and the format value %j
(the day number in the year):
如果需要计算更长的(最多 364 天)时差,我们必须使用(某些)年份的开始作为参考和格式值%j
(一年中的天数):
Similar to:
相似:
string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"
Output:
026 days 00:02:14.340003400
Sadly, in this case, we need to manually subtract 1
ONE from the number of days.
The date command view the first day of the year as 1.
Not that difficult ...
遗憾的是,在这种情况下,我们需要手动1
从天数中减去ONE。date 命令将一年中的第一天视为 1。没那么难...
a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"
The use of long number of seconds is valid and documented here:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date
长秒数的使用是有效的并记录在此处:https:
//www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date
Busybox date
忙线日期
A tool used in smaller devices (a very small executable to install): Busybox.
用于较小设备的工具(要安装的非常小的可执行文件):Busybox。
Either make a link to busybox called date:
或者创建一个指向名为 date 的 busybox 的链接:
$ ln -s /bin/busybox date
Use it then by calling this date
(place it in a PATH included directory).
然后通过调用它来使用它date
(将它放在包含 PATH 的目录中)。
Or make an alias like:
或者创建一个别名,如:
$ alias date='busybox date'
Busybox date has a nice option: -D to receive the format of the input time. That opens up a lot of formats to be used as time. Using the -D option we can convert the time 10:33:56 directly:
Busybox date 有一个不错的选项:-D 接收输入时间的格式。这开辟了许多用作时间的格式。使用 -D 选项,我们可以直接转换时间 10:33:56:
date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"
And as you can see from the output of the Command above, the day is assumed to be "today". To get the time starting on epoch:
正如您从上面命令的输出中看到的那样,假定这一天是“今天”。要从 epoch 开始计时:
$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56
Busybox date can even receive the time (in the format above) without -D:
Busybox date 甚至可以在没有 -D 的情况下接收时间(以上面的格式):
$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56
And the output format could even be seconds since epoch.
并且输出格式甚至可能是自纪元以来的秒数。
$ date -u -d "1970.01.01-$string1" +"%s"
52436
For both times, and a little bash math (busybox can not do the math, yet):
两次,还有一点 bash 数学(busybox 还不能做数学):
string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))
Or formatted:
或格式化:
$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14
回答by Dorian
Here is how I did it:
这是我如何做到的:
START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int(/60)":"int(%60)}'
Really simple, take the number of seconds at the start, then take the number of seconds at the end, and print the difference in minutes:seconds.
真的很简单,在开始时取秒数,然后在结束时取秒数,并以分钟:秒为单位打印差异。
回答by nisetama
Another option is to use datediff
from dateutils
(http://www.fresse.org/dateutils/#datediff):
另一种选择是使用datediff
from dateutils
( http://www.fresse.org/dateutils/#datediff):
$ datediff 10:33:56 10:36:10
134s
$ datediff 10:33:56 10:36:10 -f%H:%M:%S
0:2:14
$ datediff 10:33:56 10:36:10 -f%0H:%0M:%0S
00:02:14
You could also use gawk
. mawk
1.3.4 also has strftime
and mktime
but older versions of mawk
and nawk
don't.
您也可以使用gawk
. mawk
1.3.4 也有strftime
和mktime
但旧版本mawk
和nawk
没有。
$ TZ=UTC0 awk 'BEGIN{print strftime("%T",mktime("1970 1 1 10 36 10")-mktime("1970 1 1 10 33 56"))}'
00:02:14
Or here's another way to do it with GNU date
:
或者这是使用 GNU 执行此操作的另一种方法date
:
$ date -ud@$(($(date -ud'1970-01-01 10:36:10' +%s)-$(date -ud'1970-01-01 10:33:56' +%s))) +%T
00:02:14
回答by Zskdan
I'd like to propose another way that avoid recalling date
command. It may be helpful in case if you have already gathered timestamps in %T
date format:
我想提出另一种避免回忆date
命令的方法。如果您已经收集了%T
日期格式的时间戳,这可能会有所帮助:
ts_get_sec()
{
read -r h m s <<< $(echo | tr ':' ' ' )
echo $(((h*60*60)+(m*60)+s))
}
start_ts=10:33:56
stop_ts=10:36:10
START=$(ts_get_sec $start_ts)
STOP=$(ts_get_sec $stop_ts)
DIFF=$((STOP-START))
echo "$((DIFF/60))m $((DIFF%60))s"
we can even handle millisecondes in the same way.
我们甚至可以用同样的方式处理毫秒。
ts_get_msec()
{
read -r h m s ms <<< $(echo | tr '.:' ' ' )
echo $(((h*60*60*1000)+(m*60*1000)+(s*1000)+ms))
}
start_ts=10:33:56.104
stop_ts=10:36:10.102
START=$(ts_get_msec $start_ts)
STOP=$(ts_get_msec $stop_ts)
DIFF=$((STOP-START))
min=$((DIFF/(60*1000)))
sec=$(((DIFF%(60*1000))/1000))
ms=$(((DIFF%(60*1000))%1000))
echo "${min}:${sec}.$ms"
回答by redolent
Here's some magic:
这里有一些魔法:
time1=14:30
time2=$( date +%H:%M ) # 16:00
diff=$( echo "$time2 - $time1" | sed 's%:%+(1/60)*%g' | bc -l )
echo $diff hours
# outputs 1.5 hours
sed
replaces a :
with a formula to convert to 1/60. Then the time calculation that is made by bc
sed
用:
公式替换 a以转换为 1/60。那么时间计算是由bc
回答by rags
As of date (GNU coreutils) 7.4 you can now use -d to do arithmetic :
截至日期(GNU coreutils)7.4,您现在可以使用 -d 进行算术运算:
$ date -d -30days
Sat Jun 28 13:36:35 UTC 2014
$ date -d tomorrow
Tue Jul 29 13:40:55 UTC 2014
The units you can use are days, years, months, hours, minutes, and seconds :
您可以使用的单位是天、年、月、小时、分钟和秒:
$ date -d tomorrow+2days-10minutes
Thu Jul 31 13:33:02 UTC 2014
回答by user3561136
Or wrap it up a bit
或者稍微包裹一下
alias timerstart='starttime=$(date +"%s")'
alias timerstop='echo seconds=$(($(date +"%s")-$starttime))'
Then this works.
然后这有效。
timerstart; sleep 2; timerstop
seconds=2
回答by mcaleaa
Following on from Daniel Kamil Kozar's answer, to show hours/minutes/seconds:
继 Daniel Kamil Kozar 的回答之后,显示小时/分钟/秒:
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
So the full script would be:
所以完整的脚本将是:
date1=$(date +"%s")
date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
回答by Zoltan K.
Here is a solution using only the date
commands capabilities using "ago", and not using a second variable to store the finish time:
这是一个仅date
使用“ago”命令功能的解决方案,而不使用第二个变量来存储完成时间:
#!/bin/bash
# save the current time
start_time=$( date +%s.%N )
# tested program
sleep 1
# the current time after the program has finished
# minus the time when we started, in seconds.nanoseconds
elapsed_time=$( date +%s.%N --date="$start_time seconds ago" )
echo elapsed_time: $elapsed_time
this gives:
这给出:
$ ./time_elapsed.sh
elapsed_time: 1.002257120