使用bash脚本从json计算累积时间

时间:2019-01-09 14:35:59

标签: json bash date jq

我有json格式的数据,该数据记录带有事件(输入/输出)的时间戳记(24小时格式的hh:mm)。我的目标是合计“入”事件和下一个“出”事件之间的所有时间差。

为简单起见,我假设没有不一致的地方(第一个元素始终是一个“ IN”,每个“ IN”之后都是一个“ OUT”)。例外:如果最后一个元素是“ IN”,则必须在当前时间和最后一个“ IN”事件的时间戳之间进行计算。

到目前为止,这是我的脚本,可以计算所有时间跨度,也可以计算OUT和IN事件之间的时间跨度。但是我只需要那些介于IN和OUT事件之间的对象。

欢迎任何提示,在这里可能更有用!

#!/bin/bash

JSON='{ "times": [ [ "7:43", "IN" ], [ "8:26", "OUT" ], [ "8:27", "IN" ], [ "9:12", "OUT" ], [ "9:14", "IN" ], [ "9:22", "OUT" ], [ "9:23", "IN " ], [ "12:12", "OUT" ], [ "13:12", "IN" ] ]}'
IN_TIMES=$(jq '.times | to_entries | .[] | select(.value[1]| tostring | contains("IN")) | .value[0]' <<< "$JSON")
OUT_TIMES=$(jq '.times | to_entries | .[] | select(.value[1]| tostring | contains("OUT")) | .value[0]' <<< "$JSON")
ALL_TIMES=$(jq -r '.times| to_entries | .[] | .value[0]' <<< "$JSON")

prevtime=0
count=0
for i in $(echo $ALL_TIMES | sed "s/ / /g")
do
    if [[ "$count" -eq 0 ]]; then
     (( count++ ))
     prevtime=$i
     continue
    else
     (( count++ ))
    fi

    time1=`date +%s -d ${prevtime}`
    time2=`date +%s -d ${i}`
    diffsec=`expr ${time2} - ${time1}`

    echo From $prevtime to $i: `date +%H:%M -ud @${diffsec}`
    prevtime=$i

done

3 个答案:

答案 0 :(得分:2)

这是仅调用jq一次的only-jq解决方案。 但是请注意,它可能需要进行调整,以考虑时区因素,错误处理以及其他可能的复杂性:

def mins: split(":") | map(tonumber) | .[0] * 60 + .[1];

def diff: (.[1] - .[0]) | if . >= 0 then . else 24*60 + . end;

def now_mins: now | gmtime | .[3] * 60 + .[4];

def pairs:
  range(0; length; 2) as $i | [.[$i], .[$i+1] ];

def sigma(s): reduce s as $s (0; . + $s);

.times
| map( .[0] |= mins )
| if .[-1][1] == "IN" then . + [ [now_mins, "OUT"] ] else . end
| sigma(pairs | map(.[0]) | diff)

答案 1 :(得分:1)

由于您可以测量分钟以内的时间,因此足以计算分钟而不会弄乱命令date。我有一个awk解决方案:

awk -F: -vIRS=" " -vfmt="From %5s to %5s: %4u minutes\n" \
  '{this=$1*60+$2}a{printf(fmt,at,$0,this-a);a=0;next}{a=this;at=$0}\
  END{if(a){$0=strftime("%H:%M");printf(fmt,at,$0,$1*60+$2-a)}}' <<<"$ALL_TIMES"

通过将冒号定义为字段分隔符并将空格定义为记录分隔符来工作。这样,我们每次都获得一个包含两个字段的单独记录。然后

  • {this=$1*60+$2}:我们计算当前记录中有多少分钟,并将它们放入变量this中。
  • a{printf(fmt,at,$0,this-a);a=0;next}:如果(最初为空)变量a不为null也不为零,则我们正在读取OUT条目,因此我们打印所需内容,设置{{1} }设为零,因为下一个字段将是a条目,我们继续下一个记录。
  • IN:否则,我们正在读取一个{a=this;at=$0}条目,并将IN设置为其分钟,并将a设置为其字符串表示(需要将其打印为之前的情况)。
  • at:最后,如果我们还有一些悬而未决的END{if(a){$0=strftime("%H:%M");printf(fmt,at,$0,$1*60+$2-a)}}数据,则可以将IN设置为正确的当前时间,并打印我们想要的内容。

全部完成。

答案 2 :(得分:0)

有了Xidel和一点XQuery魔术,这很简单:

#!/bin/bash

JSON='{"times": [["7:43", "IN"], ["8:26", "OUT"], ["8:27", "IN"], ["9:12", "OUT"], ["9:14", "IN"], ["9:22", "OUT"], ["9:23", "IN "], ["12:12", "OUT"], ["13:12", "IN"]]}'

xidel -s - --xquery '
  let $in:=$json/(times)()[contains(.,"IN")](1) ! time(
        substring(
          "00:00:00",
          1,
          8-string-length(.)
        )||.
      ),
      $out:=$json/(times)()[contains(.,"OUT")](1) ! time(
        substring(
          "00:00:00",
          1,
          8-string-length(.)
        )||.
      )
  for $x at $i in $out return
  concat(
    "From ",
    $in[$i],
    " to ",
    $x,
    ": ",
    $x - $in[$i] + time("00:00:00")
  )
' <<< "$JSON"

$in

00:07:43
00:08:27
00:09:14
00:09:23
00:13:12

$out

00:08:26
00:09:12
00:09:22
00:12:12

输出:

From 00:07:43 to 00:08:26: 00:00:43
From 00:08:27 to 00:09:12: 00:00:45
From 00:09:14 to 00:09:22: 00:00:08
From 00:09:23 to 00:12:12: 00:02:49