Bash: Datums- / Uhrzeit-Mathematikprobleme

Oct 02 2020

Das folgende Skript benötigt zwei Eingaben sourceFile="${1}"und trimFile="${2}". Dies $sourceFileist eine Videodatei, die basierend auf den Daten in der Datei in mehrere neue Dateien zugeschnitten werden soll $trimFile. Das $trimFileist eine Textdatei mit der folgenden Struktur (aktuelle Datei auf dieses Problem bezogen):

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

Das $trimFileverwendet sowohl Kommas als auch Leerzeichen als Trennzeichen im folgenden Skript. Obwohl dies ziemlich offensichtlich ist, repräsentiert jede Zeile einen Clip, der aus dem erstellt werden soll $sourceFile. Ferner ist das erste Feld die Startzeit des zu schneidenden Clips, das zweite Feld ist die Endzeit des zu schneidenden Clips und das letzte Feld ist eine Beschreibung des Clips.

Mein Problem ist nicht das Trimmen der $sourceFile. Ich versuche, die neuen Clips mit einem Datum und einer Uhrzeit relativ zum Datum der ursprünglichen Videodateien umzubenennen. Jede $sourceFileund $trimFilewird wie das folgende Beispiel genannt (die die tatsächlichen Dateinamen ich derzeit verwende): 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).mp4und 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).txt, respectively. Auch während ziemlich offensichtlich, die Datumskomponenten Year: 2017, Month: 05, Day: 16, Hour: 14, Minute: 17, und Second:22(das Datum ignorieren ist die Klammer , wie es eine alte Referenz mit dem falschen UTC eingestellt Datum ist).

Im folgenden Skript sollte ersichtlich sein, wie das Datum und die Uhrzeit in der Datei extrahiert werden und wie das Datum / die Uhrzeit in der Datei extrahiert $trimFilewerden. Um das Problem zu veranschaulichen, muss ich es sowohl mit als auch ohne bestimmte auskommentierte Zeilen zeigen. Hier ist es mit bestimmten Zeilen auskommentiert (es wird Sinn machen, wenn ich es als nächstes diskutiere).

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}"

Wenn ich das Skript in dieser Form ausführe, erhalte ich die folgende Ausgabe entsprechend den Zeilen 48-52 im Skript:

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

Wie Sie sehen können, wird das erwartete neue Datum / die erwartete neue Uhrzeit in den Zeilen 4, 9, 14 korrekt ausgegeben (ich bin mir nicht sicher, warum die letzte Zeile von $trimFilezweimal ausgegeben wird, aber das ist im Moment nicht mein Anliegen).

Das Problem liegt in der tatsächlichen Umbenennung der Datei, wenn ich die kommentierten Zeilen (Zeilen 13, 14, 54, 59) aus dem Skript entferne, sodass das Skript jetzt folgendermaßen aussieht:

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: 

Die vom Skript erstellten Dateien heißen wie folgt:

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

Offensichtlich entsprechen die Daten, die zum Benennen der Datei verwendet wurden, nicht der Ausgabe von Stout, die erstellt wurde, als ich das Video nicht neu codiert habe, sondern nur getestet habe, ob die neuen Datumsvariablen korrekt berechnet wurden.

Meine Frage läuft also darauf hinaus, warum nach einigen Datums- / Zeitberechnungen das Datum / die Uhrzeit korrekt ist, wenn es als stark wiedergegeben wird, aber grob falsch, wenn die benannten Dateien benannt werden.

Vielen Dank!

Antworten

2 KamilMaciorowski Oct 02 2020 at 13:02

Warum ist das Datum / die Uhrzeit nach einigen Datums- / Uhrzeitberechnungen korrekt, wenn sie als stark wiedergegeben werden, aber grob falsch, wenn sie zum Benennen der einmal codierten Dateien verwendet werden?

Durch Kommentieren

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

Sie haben die folgenden Zeilen erstellt, um falsche Fragmente zu extrahieren.

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

ZB $origYearerweitert sich jetzt auf /dev.

Ich würde erwarten date -d, ähnliche Fehler zu werfen date: invalid date '/dev-sh-/2 17:05:15'. Vielleicht date -dvalidiert Ihr die Eingabe nicht (oder haben Sie die Fehler ignoriert?).

Sie können dieses spezielle Problem beheben, indem Sie Teilzeichenfolgen usw. zuweisen origYear, origMonthbevor Sie den Wert von ändern sourceFile.

Brian Oct 03 2020 at 11:20

Falls in Zukunft jemand auf diesen Beitrag verweist, finden Sie unten das korrigierte, funktionierende Skript. Es gab zusätzliche Fehler, die über das hinausgingen, was in diesem Beitrag angesprochen wurde, und die ebenfalls korrigiert wurden (nämlich den Befehl ffmpeg und die Position von -ssund -tinnerhalb des Befehls.

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"