Bash: Tarih / Saat matematik problemleri

Oct 02 2020

Aşağıdaki komut dosyası iki girdi alır sourceFile="${1}"ve trimFile="${2}". , İçindeki $sourceFileverilere göre birden çok yeni dosyaya kırpılacak bir video dosyasıdır $trimFile. $trimFileAşağıdaki yapıya (bu sorunla ilgili gerçek dosya) bir metin dosyasıdır:

00:49:30,00:53:00 DescriptionA
03:33:30,03:38:40 DescriptionB
04:54:32,04:55:37 DescriptionC

$trimFileAşağıdaki komut sınırlayıcı olarak virgül ve boşluk hem de kullanır. Oldukça açık olsa da, her çizgi $sourceFile. Ayrıca, birinci alan, kırpılacak klibin başlangıç ​​zamanıdır, ikinci alan, kırpılacak klibin bitiş zamanıdır ve son alan, klibin bir açıklamasıdır.

Benim sorunum $sourceFile. Yeni klipleri, orijinal video dosyalarının tarihine göre bir tarih ve saatle yeniden adlandırmaya çalışıyorum. Her $sourceFileve $trimFile: (gerçek dosya ı halen kullanıyorum isimleri olan) şu örnekteki gibi adlandırılır 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).mp4ve 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).txtsırasıyla. Oldukça belirgin, tarih bileşenleri ise yine Year: 2017, Month: 05, Day: 16, Hour: 14, Minute: 17, ve Second:22(yanlış UTC düzeltilmiş tarih ile eski referans olarak tarih görmezden parantez).

Dosyadaki tarih / saatin nasıl çıkarıldığı ve içindeki tarih / saatin nasıl çıkarıldığı aşağıdaki komut dosyasında açıkça görülmelidir $trimFile. Sorunu açıklamak için, onu hem yorumlanmış hem de açıklamalı olarak göstermem gerekiyor. Burada, yorumlanmış belirli satırlarla (daha sonra tartıştığım gibi mantıklı olacak).

01: sourceFile="${1}"
02: trimFile="${2}" 03: 04: IFS=$'\n'
05: 
06: dos2unix "$trimFile" 07: numberOfSegments=`cat "$trimFile" | wc -l`
08: numberOfSegments=$((numberOfSegments + 1)) 09: extension=`echo "$sourceFile" | awk -F'.' '{print $NF}'` 10: 11: base=`echo "$sourceFile" | sed -e "s|.$extension||g"` 12: 13: #~/bin/ffmpeg -i "${sourceFile}" -c:v copy "/dev/shm/$base.${extension}"
14: #sourceFile="/dev/shm/$base.${extension}"
15: 
16: # File date/time information
17: origYear="${sourceFile:0:4}" 18: origMonth="${sourceFile:5:2}"
19: origDay="${sourceFile:8:2}" 20: origHour="${sourceFile:11:2}"
21: origMinute="${sourceFile:14:2}" 22: origSecond="${sourceFile:17:2}"
23: 
24: origDate="${origYear}-${origMonth}-${origDay} ${origHour}:${origMinute}:${origSecond}"
25: 
26: for (( i=1;i<="$numberOfSegments";i++ )) 27: do 28: 29: lineEntry=`cat "$trimFile" | head -"$i" | tail -1` 30: 31: startHour=`echo "$lineEntry" | awk -F'[:,]' '{print $1}'` 32: startMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $2}'` 33: startSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $3}'` 34: 35: endHour=`echo "$lineEntry" | awk -F'[:,]' '{print $4}'` 36: endMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $5}'` 37: endSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $6}'` 38: 39: description=`echo "$lineEntry" | awk -F'[:, ]' '{print $7}'` 40: 41: beginSeconds=`awk "BEGIN {print ($startHour*3600+$startMinute*60+$startSecond)}"`
42:         stopSeconds=`awk "BEGIN {print ($endHour*3600+$endMinute*60+$endSecond)}"` 43: duration=`awk "BEGIN {print $stopSeconds-$beginSeconds}"` 44: 45: newDate=$(date -d "@$(( $(date -d "${origDate}" +%s) + ${beginSeconds}))" +'%Y-%m-%d_%H-%M-%S')
46: 
47:         new="${newDate}_${description}"
48:         echo "${lineEntry}" 49: echo "${origDate}"
50:         echo "${beginSeconds}" 51: echo "${new}"
52:         echo ""
53:         
54:         #~/bin/ffmpeg -n -vsync drop -fflags +genpts -i "$sourceFile" -ss "$beginSeconds" -t "$duration" -c:v libx265 -crf 27 -preset slow "$new.mkv"
55: 
56: 
57:     done
58:     
59: #rm "/dev/shm/${base}.${extension}"

Komut dosyasını bu formda çalıştırdığımda, komut dosyasındaki 48-52 satırlarına karşılık gelen aşağıdaki çıktıyı alıyorum:

01: 00:49:30,00:53:00 DescriptionA
02: 2017-05-15 14:17:22
03: 2970
04: 2017-05-15_15-06-52_DescriptionA
05: 
06: 03:33:30,03:38:40 DescriptionB
07: 2017-05-15 14:17:22
08: 12810
09: 2017-05-15_17-50-52_DescriptionB
10: 
11: 04:54:32,04:55:37 DescriptionC
12: 2017-05-15 14:17:22
13: 17672
14: 2017-05-15_19-11-54_DescriptionC
15: 
16: 04:54:32,04:55:37 DescriptionC
17: 2017-05-15 14:17:22
18: 17672
19: 2017-05-15_19-11-54_DescriptionC

Gördüğünüz gibi, beklenen yeni tarih / saat 4,9,14 satırlarında doğru bir şekilde çıktı (son satırının neden $trimFileiki kez çıktı aldığından emin değilim , ancak şu anda bu benim endişem değil).

Sorun şu ki, komut dosyasından açıklamalı satırları (satır 13, 14, 54, 59) kaldırdığımda dosyanın asıl yeniden adlandırılması var, böylece komut dosyası şimdi şöyle görünüyor:

01: sourceFile="${1}"
02: trimFile="${2}" 03: 04: IFS=$'\n'
05: 
06: dos2unix "$trimFile" 07: numberOfSegments=`cat "$trimFile" | wc -l`
08: numberOfSegments=$((numberOfSegments + 1)) 09: extension=`echo "$sourceFile" | awk -F'.' '{print $NF}'` 10: 11: base=`echo "$sourceFile" | sed -e "s|.$extension||g"` 12: 13: ~/bin/ffmpeg -i "${sourceFile}" -c:v copy "/dev/shm/$base.${extension}"
14: sourceFile="/dev/shm/$base.${extension}"
15: 
16: # File date/time information
17: origYear="${sourceFile:0:4}" 18: origMonth="${sourceFile:5:2}"
19: origDay="${sourceFile:8:2}" 20: origHour="${sourceFile:11:2}"
21: origMinute="${sourceFile:14:2}" 22: origSecond="${sourceFile:17:2}"
23: 
24: origDate="${origYear}-${origMonth}-${origDay} ${origHour}:${origMinute}:${origSecond}"
25: 
26: for (( i=1;i<="$numberOfSegments";i++ )) 27: do 28: 29: lineEntry=`cat "$trimFile" | head -"$i" | tail -1` 30: 31: startHour=`echo "$lineEntry" | awk -F'[:,]' '{print $1}'` 32: startMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $2}'` 33: startSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $3}'` 34: 35: endHour=`echo "$lineEntry" | awk -F'[:,]' '{print $4}'` 36: endMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $5}'` 37: endSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $6}'` 38: 39: description=`echo "$lineEntry" | awk -F'[:, ]' '{print $7}'` 40: 41: beginSeconds=`awk "BEGIN {print ($startHour*3600+$startMinute*60+$startSecond)}"`
42:         stopSeconds=`awk "BEGIN {print ($endHour*3600+$endMinute*60+$endSecond)}"` 43: duration=`awk "BEGIN {print $stopSeconds-$beginSeconds}"` 44: 45: newDate=$(date -d "@$(( $(date -d "${origDate}" +%s) + ${beginSeconds}))" +'%Y-%m-%d_%H-%M-%S')
46: 
47:         new="${newDate}_${description}"
48:         echo "${lineEntry}" 49: echo "${origDate}"
50:         echo "${beginSeconds}" 51: echo "${new}"
52:         echo ""
53:         
54:         ~/bin/ffmpeg -n -vsync drop -fflags +genpts -i "$sourceFile" -ss "$beginSeconds" -t "$duration" -c:v libx265 -crf 27 -preset slow "$new.mkv"
55: 
56: 
57:     done
58:     
59: rm "/dev/shm/${base}.${extension}"
60: 
61: 

Komut dosyası tarafından oluşturulan dosyalar şu şekilde adlandırılır:

1969-12-31_19-49-30_DescriptionA.mkv
1969-12-31_22-33-30_DescriptionB.mkv
1969-12-31_23-54-32_DescriptionC.mkv

Açıkçası, dosyayı adlandırmak için kullanılan tarihler, videoyu yeniden kodlamadığım zaman oluşturulan çıktı gibi bir şey değil, sadece yeni tarih değişkenlerinin doğru hesaplandığını test etti.

Öyleyse, sorum şuna indirgeniyor, neden bir tarih / saat matematiği yaptıktan sonra, tarih / saat sağlam olarak yankılandığında doğru, ancak bir kez kodlandıktan sonra dosyaları adlandırmak için kullanıldığında büyük ölçüde yanlış.

Teşekkürler!

Yanıtlar

2 KamilMaciorowski Oct 02 2020 at 13:02

Tarih / saat hesaplamalarını yaptıktan sonra neden tarih / saat sağlam bir şekilde yankılandığında doğru, ancak bir kez kodlandıktan sonra dosyaları adlandırmak için kullanıldığında büyük ölçüde yanlış?

Yorumsuz olarak

sourceFile="/dev/shm/$base.${extension}"

takip eden çizgileri yanlış parçalar ayıkladınız.

origYear="${sourceFile:0:4}" origMonth="${sourceFile:5:2}"
…

Örneğin, şimdi olarak $origYeargenişler /dev.

date -dBenzer hatalar atmayı beklerdim date: invalid date '/dev-sh-/2 17:05:15'. Belki sizin date -dgirişinizi doğrulamıyor (veya hataları görmezden mi geldiniz?).

Değerini değiştirmeden önce alt dizeler origYear, origMonthvb. Atayarak bu sorunu çözebilirsiniz sourceFile.

Brian Oct 03 2020 at 11:20

Gelecekte birisinin bu gönderiye başvurması durumunda, aşağıda düzeltilmiş, çalışan komut dosyası verilmiştir. Bu yazıda ele alınanların ötesinde düzeltilen başka hatalar da vardı (yani, ffmpeg komutu ve komutun -ssve -tiçindeki konumu .

01: sourceFile="${1}" 02: trimFile="${2}"
03: 
04: IFS=$'\n' 05: 06: dos2unix "$trimFile"
07: numberOfSegments=`cat "$trimFile" | wc -l` 08: numberOfSegments=$((numberOfSegments))
09: extension=`echo "$sourceFile" | awk -F'.' '{print $NF}'`
10: 
11: base=`echo "$sourceFile" | sed -e "s|.$extension||g"`
12: 
13: ~/bin/ffmpeg -i "${sourceFile}" -c:v copy "/dev/shm/$base.mkv"
14: sourceFile="/dev/shm/$base.mkv" 15: 16: # File date/time information 17: origYear="${sourceFile:9:4}"
18: origMonth="${sourceFile:14:2}" 19: origDay="${sourceFile:17:2}"
20: origHour="${sourceFile:20:2}" 21: origMinute="${sourceFile:23:2}"
22: origSecond="${sourceFile:26:2}" 23: 24: origDate="${origYear}-${origMonth}-${origDay} ${origHour}:${origMinute}:${origSecond}" 25: 26: for (( i=1;i<="$numberOfSegments";i++ ))
27:     do
28: 
29:         lineEntry=`cat "$trimFile" | head -"$i" | tail -1`
30: 
31:         startHour=`echo "$lineEntry" | awk -F'[:, ]' '{print $1}'`
32:         startMinute=`echo "$lineEntry" | awk -F'[:, ]' '{print $2}'`
33:         startSecond=`echo "$lineEntry" | awk -F'[:, ]' '{print $3}'`
34: 
35:         endHour=`echo "$lineEntry" | awk -F'[:, ]' '{print $4}'`
36:         endMinute=`echo "$lineEntry" | awk -F'[:, ]' '{print $5}'`
37:         endSecond=`echo "$lineEntry" | awk -F'[:, ]' '{print $6}'`
38: 
39:         description=`echo "$lineEntry" | awk -F'[:, ]' '{print $7}'`
40: 
41:         beginSeconds=`awk "BEGIN {print ($startHour*3600+$startMinute*60+$startSecond)}"` 42: stopSeconds=`awk "BEGIN {print ($endHour*3600+$endMinute*60+$endSecond)}"`
43:         duration=`awk "BEGIN {print $stopSeconds-$beginSeconds}"`
44: 
45:         newDate=$(date -d "@$(( $(date -d "${origDate}" +%s) + ${beginSeconds}))" +'%Y-%m-%d_%H-%M-%S') 46: newDate=`echo ${newDate}`
47:         
48:         new="${newDate}_${description}"
49:         
50:         ~/bin/ffmpeg -n -ss "$beginSeconds" -t "$duration" -i "$sourceFile" -c:v copy "$new.mkv"
51: 
52:     done
53:     
54: rm "/dev/shm/${base}.mkv"