我使用date +"%T"
打印出开始和结束时间,结果类似于:
10:33:56
10:36:10
我该如何计算并打印出这两者之间的差异?
我希望得到类似这样的结果:
2m 14s
Bash有一个方便的SECONDS
内置变量,用于跟踪自 shell 启动以来经过的秒数。当对其进行赋值时,此变量保留其属性,并且赋值后返回的值是自赋值以来经过的秒数加上所赋的值。
因此,在启动计时事件之前,您可以将SECONDS
设置为0,然后在事件结束后读取SECONDS
,并在显示之前执行时间算术运算。
#!/usr/bin/env bash
SECONDS=0
# do some work
duration=$SECONDS
echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."
由于这个解决方案不依赖于date +%s
(它是GNU扩展),因此它可以在所有受Bash支持的系统上使用。
date -u -d @"$diff" +'%-Mm %-Ss'
来骗过date
让它帮你进行时间算术运算(它会将$diff
解释为自纪元以来的秒数,并计算出UTC中的分钟和秒数)。虽然这可能并不比原来更优雅,但它更加难以理解。:-P - ruakhecho "$(($diff / 3600)) 小时,$((($diff / 60) % 60)) 分钟和 $(($diff % 60)) 秒已经过去。"
- chus$(date -u +%s)
。注意恶意跳秒 ;) - Tinounset SECONDS
之后,它就消失了:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
- Tino为了测量经过的时间(以秒为单位),我们需要:
printf
命令可以直接实现:$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45
同样地
$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34
但是对于超过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
对于细节爱好者,来自bash-hackers.org的内容:
%(FORMAT)T
输出使用FORMAT作为格式字符串对strftime(3)
进行格式化得到的日期时间字符串。相关参数是自纪元以来的秒数,或-1(当前时间)或-2(shell启动时间)。如果没有提供相应的参数,则默认使用当前时间。
因此,您可能只需调用textifyDuration $elpasedseconds
,其中textifyDuration
是另一种持续时间打印的实现:
textifyDuration() {
local duration=$1
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
内一步完成所有操作:
date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"
是的,在命令字符串中有一个0
零。它是必需的。
这是假设您可以将date +"%T"
命令更改为date +"%s"
命令,以便值将以秒为单位存储(打印)。
请注意,该命令仅限于:
$StartDate
和$FinalDate
秒数的正值。$FinalDate
中的值大于(比时间晚)$StartDate
。10:33:56
,那就将其转换为秒。另外,“秒”这个词可以缩写为“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"
这个概念可以扩展到纳秒,像这样:
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"
如果需要计算更长(长达364天)的时间差异,我们必须使用某年的开始作为参考,并使用格式值%j
(一年中的日期编号):
类似于:
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
不幸的是,在这种情况下,我们需要手动从天数中减去1
ONE。
日期命令将一年的第一天视为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[@]}"
使用大量秒数是有效的并且在21.1.7 日期示例中有记录。
这是一个在小型设备中使用的工具(非常小的可执行文件):BusyBox。
要么创建一个名为date的BusyBox链接:
$ ln -s /bin/busybox date
通过调用此 date
(将其放置在路径包含的目录中)来使用它。
或者创建一个类似的别名:
$ alias date='busybox date'
BusyBox date 命令有一个很好的选项:-D,可以接收输入时间的格式。
这打开了许多可用作时间的格式。 使用 -D 选项,我们可以直接转换时间 10:33:56:
date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"
正如你从上面命令的输出中所看到的,日期被假定为“今天”。要从纪元开始获取时间:
$ 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甚至可以在没有-D的情况下接收时间(以上述格式):
$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56
而且输出格式甚至可以是自纪元以来的秒数。
$ date -u -d "1970.01.01-$string1" +"%s"
52436
对于两个时间点,以及一些简单的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 ))
或格式化:
$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14
%(FORMAT)T
字符串。来自bash-hackers.org的解释是:%(FORMAT)T
输出使用FORMAT
作为strftime(3)格式字符串生成的日期时间字符串。相关参数是自纪元以来的秒数,或-1(当前时间)或-2(shell启动时间)。如果没有提供相应的参数,则默认使用当前时间。这很深奥。在这种情况下,%s
作为提供给strftime(3)
的参数表示“自纪元以来的秒数”。 - David Tonhofer这是我是如何做到的:
START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int($1/60)":"int($1%60)}'
这很简单。在开始时记录秒数,在结束时记录秒数,然后打印出以分钟:秒为单位的差值。
awk
的好处,因为Bash同样能很好地处理整数运算。 - Daniel Kamil Kozarawk
命令而不是bash(即使只是因为awk在其他Shell中也可以工作)。我更喜欢这个解决方案。但那只是我的个人意见。 - rpsmlprintf
函数;输出小时、分钟和秒数:printf '%02dh:%02dm:%02ds\n' $(($secs/3600)) $(($secs%3600/60)) $(($secs%60))
- Villedateutils
中的datediff
函数(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
你也可以使用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
或者这里有另外一种使用 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
date
方法这样的东西。太聪明了。 - Haohmarudateutils
之后,没有datediff
命令 - 必须使用dateutils.ddiff
。 - Suzana我想提出另一种避免调用date
命令的方法。如果您已经使用%T
日期格式收集了时间戳,这可能会有所帮助:
ts_get_sec()
{
read -r h m s <<< $(echo $1 | 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"
ts_get_msec()
{
read -r h m s ms <<< $(echo $1 | 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"
$((10#$h))
、$((10#$m))
和 $((10#$s))
来解决问题。 - Niklas在Daniel Kamil Kozar的回答的基础上,展示小时/分钟/秒:
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
因此完整的脚本将是:
date1=$(date +"%s")
date2=$(date +"%s")
DIFF=$(($date2-$date1))
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
-d
进行算术运算:$ date -d -30days
Sat Jun 28 13:36:35 UTC 2014
$ date -d tomorrow
Tue Jul 29 13:40:55 UTC 2014
$ date -d tomorrow+2days-10minutes
Thu Jul 31 13:33:02 UTC 2014
这里是一个仅使用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
这将会得到:
$ ./time_elapsed.sh
elapsed_time: 1.002257120
printf
来格式化为两位小数。例如:% echo 经过时间:$(printf'%.2f' $ {elapsed_time})秒 - Timothy C. Quinndate
命令可以为您提供差异并为您格式化它(显示 OS X 选项)
date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) +%T
# 00:02:14
date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
+'%-Hh %-Mm %-Ss'
# 0h 2m 14s
有些字符串处理可以去除那些空值。
date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
+'%-Hh %-Mm %-Ss' | sed "s/[[:<:]]0[hms] *//g"
# 2m 14s
如果您首先放置较早的时间,这将无法正常工作。如果您需要处理该问题,请将$(($(date ...) - $(date ...)))
更改为$(echo $(date ...) - $(date ...) | bc | tr -d -)
time
命令? - anishsanedate +%s
,然后相减以获取秒数差异。 - roblogic