Bash:日期/时间数学问题

Bash:日期/时间数学问题

以下脚本接受两个输入,sourceFile="${1}"trimFile="${2}"$sourceFile是一个视频文件,将根据 中的数据将其修剪为多个新文件$trimFile$trimFile是一个具有以下结构的文本文件(与此问题相关的实际文件):

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

$trimFile在下面的脚本中,使用逗号和空格作为分隔符。虽然相当明显,但每一行都代表要从中创建的剪辑。$sourceFile此外,第一个字段是要修剪的剪辑的开始时间,第二个字段是要修剪的剪辑的结束时间,最后一个字段是剪辑的描述。

我的问题不是剪辑$sourceFile。我试图用相对于原始视频文件日期的日期和时间重命名新剪辑。每个$sourceFile和 的$trimFile命名方式如下例所示(这是我目前使用的实际文件名):2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).mp42017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).txt。同样,虽然相当明显,但日期部分是Year: 2017Month: 05Day: 16Hour: 14Minute: 17Second:22(忽略括号中的日期,因为它是带有错误 UTC 调整日期的旧参考)。

下面的脚本应该可以清楚地说明如何提取文件中的日期/时间以及如何$trimFile提取文件中的日期/时间。为了说明这个问题,我需要展示注释掉和不注释掉某些行的情况。下面是注释掉某些行的情况(我接下来讨论时会讲得通)。

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

当我以这种形式执行脚本时,我得到与脚本中的第 48-52 行对应的以下输出:

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

如您所见,预期的新日期/时间在第 4、9、14 行正确输出(我不确定为什么最后一行输出$trimFile两次,但这不是我目前关心的)。

问题在于当我从脚本中删除注释行(第 13、14、54、59 行)时文件的实际重命名,因此脚本现在如下所示:

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: 

该脚本创建的文件名称如下:

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

显然,用于命名文件的日期与我没有重新编码视频时创建的 stout 输出完全不同,而只是测试新的日期变量是否被正确计算。

所以,我的问题归结为:为什么在进行一些日期/时间计算之后,日期/时间在回显到 stout 时是正确的,但在编码后用于命名文件时却严重错误。

谢谢!

答案1

为什么在进行一些日期/时间计算之后,日期/时间在回显到 stout 时是正确的,但在编码后用于命名文件时却严重错误?

通过取消注释

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

您让下面的几行提取了错误的片段。

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

例如现在$origYear扩展为/dev

我预计date -d会抛出类似这样的错误date: invalid date '/dev-sh-/2 17:05:15'。也许您date -d没有验证其输入(或者您忽略了错误?)。

您可以在更改 的值之前通过将子字符串分配给 等来解决这个特定问题origYearorigMonthsourceFile

答案2

如果将来有人引用此帖子,下面是更正后的工作脚本。除了本文中提到的错误外,还更正了其他错误(即 ffmpeg 命令以及命令中的-ss位置)。-t

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"

相关内容