如何使用 bash 计算部分毫秒的时间总和?

如何使用 bash 计算部分毫秒的时间总和?

如何将示例 00:03:03.333、00:01:01.111 和 00:02:02.222 的总和放入变量中

EPOCH='jan 1 1970'
offset=$EPOCH

lsdvd -c VIDEO_TS | grep "Length:" | grep "Chapter:" | awk '{print($4)}' | while read -r len
do
  len=${len:0:12}
  offset="$(date -u -d "$EPOCH $len" +%T.%3N) + $(date -u -d "$offset" +%T.%3N)"
  echo "need sum" $offset "then" ffmpeg -ss $offset ... -t $len ...
done

答案1

sh 只能进行整数算术,因此您必须使用其他东西,例如bc(或可以进行非整数算术的语言,例如 awk 或 perl)。

要使用bc,首先必须将 %H:%M:%S.%3N 格式转换为秒(包括小数部分),然后通过管道将其传输到bc

例如:

#!/bin/bash

epoch='1970-01-01'

len1='00:01:01.111'
len2='00:02:02.222'

# convert to seconds
len1_s=$(date -u -d "$epoch $len1" '+%s.%3N')
len2_s=$(date -u -d "$epoch $len2" '+%s.%3N')

# add the seconds
sum_s=$( echo "$len1_s + $len2_s" | bc )

echo $len1_s + $len2_s = $sum_s

# convert back to H:M:S.N
sum=$(date -u -d "@$sum_s" '+%T.%3N')

echo $len1 + $len2 = $sum

输出:

$ ./sum.sh 
61.111 + 122.222 = 183.333
00:01:01.111 + 00:02:02.222 = 00:03:03.333

我将在 while 循环中将其实现留给您......但我会注意到它lsdvd有一些有用的输出选项,可以在几秒钟内为您提供章节长度(因此您不需要转换它们)。最有用的可能是 XML 输出选项,然后可以使用它进行处理xmlstarletxml2提取章节长度。

例如,这里的lsdvdXML 输出转换为面向行的格式(适合使用 awk 或其他方式处理),使用xml2

这是一个约 48 分钟的视频,共有 11 个章节,其中大部分长度约为 5 分钟(300 秒)。

$ lsdvd -c -Ox '/path/to/some/dvd.iso'  | xml2
/lsdvd/device=/path/to/some/dvd.iso
/lsdvd/title=TITLE
/lsdvd/vmg_id=DVDVIDEO-VMG
/lsdvd/provider_id=PROVIDER
/lsdvd/track/ix=1
/lsdvd/track/length=2874.667
/lsdvd/track/vts_id=DVDVIDEO-VTS
/lsdvd/track/chapter/ix=1
/lsdvd/track/chapter/length=299.934
/lsdvd/track/chapter/startcell=1
/lsdvd/track/chapter
/lsdvd/track/chapter/ix=2
/lsdvd/track/chapter/length=299.500
/lsdvd/track/chapter/startcell=2
/lsdvd/track/chapter
/lsdvd/track/chapter/ix=3
/lsdvd/track/chapter/length=300.000
/lsdvd/track/chapter/startcell=3
[...]
/lsdvd/track/chapter/ix=11
/lsdvd/track/chapter/length=176.734
/lsdvd/track/chapter/startcell=11
/lsdvd/longest_track=1

这是一个很多与默认的人类可读输出相比,在脚本中更容易处理lsdvd。例如,如果我只想以秒为单位的章节长度:

$ lsdvd -c -Ox /path/to/some/dvd.iso' | xml2 | \
     awk -F'[/=]' '$5 == "length" { print $6 }'
299.934
299.500
300.000
299.500
300.000
299.500
299.500
300.000
299.500
0.500
176.734

我还要指出,如果您使用 awk,则不需要 grep。因此,如果您想使用人类可读的输出,您的grep | grep | awk(以及len=${len:0:12}去除尾随逗号)可以减少为:

lsdvd -c VIDEO_TS | awk -F' +|,' '/Chapter:/ && /Length:/ { print $5 }' | \
  while read len

字段分隔符此处定义为一个或多个空格或逗号。

$ lsdvd -c '/path/to/some/dvd.iso' | \
    awk -F' +|,' '/Chapter:/ && /Length:/ { print $5 }'
00:04:59.934
00:04:59.500
00:05:00.000
00:04:59.500
00:05:00.000
00:04:59.500
00:04:59.500
00:05:00.000
00:04:59.500
00:00:00.500
00:02:56.734

相关内容