Bash: problemas matemáticos de fecha / hora
La siguiente secuencia de comandos toma dos entradas sourceFile="${1}"y trimFile="${2}". El $sourceFilees un archivo de video que se recortará en varios archivos nuevos según los datos del $trimFile. El $trimFilees un archivo de texto con la siguiente estructura (archivo real relacionado con este problema):
00:49:30,00:53:00 DescriptionA
03:33:30,03:38:40 DescriptionB
04:54:32,04:55:37 DescriptionC
El $trimFileusa comas y espacios como delimitadores en el siguiente script. Aunque es bastante obvio, cada línea representa un clip que se creará a partir del archivo $sourceFile. Además, el primer campo es la hora de inicio del clip que se va a recortar, el segundo campo es la hora de finalización del clip que se va a recortar y el último campo es una descripción del clip.
Mi problema no es el recorte del $sourceFile. Estoy intentando cambiar el nombre de los nuevos clips con una fecha y hora relativas a la fecha de los archivos de video originales. Cada uno $sourceFiley $trimFiletiene el nombre del siguiente ejemplo (que son los nombres de archivo reales que estoy usando actualmente): 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).mp4y 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).txt, respectivamente. Una vez más, mientras que los componentes de la fecha, lugar son obvias Year: 2017, Month: 05, Day: 16, Hour: 14, Minute: 17, y Second:22(ignore la fecha es el paréntesis, ya que es una antigua referencia a la fecha de UTC mal ajustado).
Debería ser evidente en el script a continuación cómo se extrae la fecha / hora en el archivo, así como cómo se extrae la fecha / hora dentro del archivo $trimFile. Para ilustrar el problema, necesito mostrarlo con y sin ciertas líneas comentadas. Aquí está con ciertas líneas comentadas (tendrá sentido como lo discutiré a continuación).
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}"
Cuando ejecuto el script de esta forma, obtengo el siguiente resultado correspondiente a las líneas 48-52 del 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
Como puede ver, la nueva fecha / hora esperada se muestra correctamente en las líneas 4, 9, 14 (no estoy seguro de por qué la última línea se $trimFilemuestra dos veces, pero esa no es mi preocupación en este momento).
El problema radica en el cambio de nombre real del archivo cuando elimino las líneas comentadas (líneas 13, 14, 54, 59) del script para que el script ahora se vea así:
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:
Los archivos creados por el script se denominan de la siguiente manera:
1969-12-31_19-49-30_DescriptionA.mkv
1969-12-31_22-33-30_DescriptionB.mkv
1969-12-31_23-54-32_DescriptionC.mkv
Obviamente, las fechas utilizadas para nombrar el archivo no se parecen en nada a la salida de stout creada cuando no volví a codificar el video, sino que simplemente probé que las nuevas variables de fecha se calculaban correctamente.
Entonces, mi pregunta se reduce a esto, ¿por qué, después de hacer algunos cálculos de fecha / hora, la fecha / hora es correcta cuando se repite en forma sólida pero muy incorrecta cuando se usa para nombrar los archivos una vez codificados?
¡Gracias!
Respuestas
¿Por qué, después de hacer algunos cálculos de fecha / hora, la fecha / hora es correcta cuando se repite a stout pero es extremadamente incorrecta cuando se usa para nombrar los archivos una vez codificados?
Al descomentar
sourceFile="/dev/shm/$base.${extension}"
hiciste que las líneas que siguen extraigan fragmentos incorrectos.
origYear="${sourceFile:0:4}" origMonth="${sourceFile:5:2}"
…
Por ejemplo, ahora se $origYearexpande a /dev.
Esperaría date -darrojar errores similares a date: invalid date '/dev-sh-/2 17:05:15'. Tal vez tu date -dno valida su entrada (¿o ignoraste los errores?).
Puede solucionar este problema en particular asignando subcadenas a origYear, origMonthetc. antes de cambiar el valor de sourceFile.
En caso de que alguien haga referencia a esta publicación en el futuro, a continuación se muestra el guión de trabajo corregido. Hubo errores adicionales más allá de lo que se abordó en esta publicación que también se corrigieron (es decir, el comando ffmpeg y la ubicación de -ssy -tdentro 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"