Bash: problemi di matematica data / ora
Lo script seguente accetta due input sourceFile="${1}"e trimFile="${2}". Il $sourceFileè un file video che deve essere tagliato in più nuovi file in base ai dati del $trimFile. Il $trimFileè un file di testo della struttura seguente (file vero e proprio relative a questo problema):
00:49:30,00:53:00 DescriptionA
03:33:30,03:38:40 DescriptionB
04:54:32,04:55:37 DescriptionC
Il $trimFileutilizza entrambe le virgole e gli spazi come delimitatori nello script di seguito. Sebbene sia piuttosto ovvio, ogni riga rappresenta una clip da creare da $sourceFile. Inoltre, il primo campo è l'ora di inizio della clip da tagliare, il secondo campo è l'ora di fine della clip da tagliare e l'ultimo campo è una descrizione della clip.
Il mio problema non è con la rifinitura del file $sourceFile. Sto cercando di rinominare i nuovi clip con una data e un'ora relative alla data dei file video originali. Ciascuno $sourceFilee $trimFileè denominato come il seguente esempio (che sono i nomi di file effettivi che sto attualmente utilizzando): 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).mp4e 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).txt, rispettivamente. Anche in questo caso, mentre piuttosto ovvie, i componenti della data sono Year: 2017, Month: 05, Day: 16, Hour: 14, Minute: 17, e Second:22(ignora la data è la parentesi in quanto è un vecchio di riferimento con l'UTC sbagliato data di regolazione).
Dovrebbe essere evidente nello script sottostante come vengono estratte la data / ora nel file e come $trimFilevengono estratte la data / ora all'interno del file . Per illustrare il problema, devo mostrarlo sia con che senza alcune righe commentate. Eccolo con alcune righe commentate (avrà senso se ne parlerò in seguito).
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}"
Quando eseguo lo script in questa forma, ottengo il seguente output corrispondente alle righe 48-52 nello script:
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
Come puoi vedere, la nuova data / ora prevista viene visualizzata correttamente nelle righe 4,9,14 (non sono sicuro del motivo per cui l'ultima riga di $trimFileviene visualizzata due volte, ma al momento non è questo il mio problema).
Il problema sta nell'effettiva ridenominazione del file quando rimuovo le righe commentate (righe 13, 14, 54, 59) dallo script in modo che lo script ora assomigli a questo:
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:
I file creati dallo script sono denominati come segue:
1969-12-31_19-49-30_DescriptionA.mkv
1969-12-31_22-33-30_DescriptionB.mkv
1969-12-31_23-54-32_DescriptionC.mkv
Ovviamente, le date usate per nominare il file non sono affatto come l'output in stout creato quando non ho ricodificato il video ma piuttosto ho solo verificato che le nuove variabili di data fossero calcolate correttamente.
Quindi, la mia domanda si riduce a questo, perché, dopo aver fatto un po 'di matematica data / ora, la data / ora è corretta quando viene ripetuta come robusta ma grossolanamente sbagliata quando viene utilizzata per denominare i file una volta codificati.
Grazie!
Risposte
perché, dopo aver fatto un po 'di matematica data / ora, la data / ora è corretta quando viene ripetuta come stout ma grossolanamente sbagliata quando viene utilizzata per denominare i file una volta codificati?
Rimuovendo i commenti
sourceFile="/dev/shm/$base.${extension}"
hai fatto le righe che seguono estrarre frammenti sbagliati.
origYear="${sourceFile:0:4}" origMonth="${sourceFile:5:2}"
…
Ad esempio, ora si $origYearespande in /dev.
Mi aspetto date -ddi generare errori simili a date: invalid date '/dev-sh-/2 17:05:15'. Forse il tuo date -dnon convalida il suo input (o hai ignorato gli errori?).
È possibile risolvere questo particolare problema assegnando sottostringhe a origYear, origMonthecc. Prima di modificare il valore di sourceFile.
Nel caso in cui qualcuno faccia riferimento a questo post in futuro, di seguito è riportato lo script corretto e funzionante. Si sono verificati errori aggiuntivi oltre a quanto è stato affrontato in questo post che sono stati corretti (vale a dire, il comando ffmpeg e la posizione di -sse -tall'interno del comando.
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"