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

bcshは整数演算のみを実行できるため、他のもの(または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

実装はしばらくあなたに任せます...しかし、ここにはlsdvd数秒で長さを提供するいくつかの便利な出力オプションがあることに注意してください(したがって変換する必要はありません)。おそらく最も便利なのは、章の長さを処理xmlstarletまたは抽出するために使用できるXML出力オプションです。xml2

たとえば、ここのXML出力を行ベースのlsdvd形式(awkまたは他の手段を使用した処理に適しています)に変換するには、次のようにしますxml2

この動画は11チャプターで構成された約48分分の動画で、ほとんどの長さが約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

フィールド区切り文字は、ここでは 1 つ以上のスペースまたはカンマで定義されます。

$ 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

関連情報