k96*_*961 39 video shell-script
我有一个.ts
文件列表:
out1.ts ... out749.ts out8159.ts out8818.ts
Run Code Online (Sandbox Code Playgroud)
如何获得所有这些文件的总持续时间(运行时间)?
don*_*sti 76
我.ts
这里没有,但这适用于.mp4
. 使用ffprobe
(part of ffmpeg
) 获取以秒为单位的时间,例如:
ffprobe -v quiet -of csv=p=0 -show_entries format=duration Inception.mp4
275.690000
Run Code Online (Sandbox Code Playgroud)
所以对于.mp4
当前目录中的所有文件:
find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \;
149.233333
130.146667
275.690000
Run Code Online (Sandbox Code Playgroud)
然后使用paste
到输出传递到bc
并获得秒的总时间:
find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc
555.070000
Run Code Online (Sandbox Code Playgroud)
因此,对于.ts
文件,您可以尝试:
find . -maxdepth 1 -iname '*.ts' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc
Run Code Online (Sandbox Code Playgroud)
另一个适用于我这里的视频文件的工具是exiftool
,例如:
exiftool -S -n Inception.mp4 | grep ^Duration
Duration: 275.69
exiftool -q -p '$Duration#' Inception.mp4
275.69
Run Code Online (Sandbox Code Playgroud)
.mp4
当前目录中所有文件的总长度:
exiftool -S -n ./*.mp4 | awk '/^Duration/ {print $2}' | paste -sd+ -| bc
555.070000000000
exiftool -q -p '$Duration#' ./*.mp4 | awk '{sum += $0}; END{print sum}'
555.070000000000
Run Code Online (Sandbox Code Playgroud)
您还可以将输出通过管道传输到另一个命令以将总数转换为DD:HH:MM:SS
,请参阅此处的答案。
或者为此使用exiftool
's internal ConvertDuration
(不过您需要一个相对较新的版本):
exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)
}' ./*.mp4| tail -n1
0:09:15
Run Code Online (Sandbox Code Playgroud)
这将使用ffmpeg
并打印总秒数的超时时间:
times=()
for f in *.ts; do
_t=$(ffmpeg -i "$f" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
times+=("$_t")
done
echo "${times[@]}" | sed 's/ /+/g' | bc
Run Code Online (Sandbox Code Playgroud)
解释:
for f in *.ts; do
迭代每个以“.ts”结尾的文件
ffmpeg -i "$f" 2>&1
将输出重定向到 stderr
grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' '
隔离时间
awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }'
将时间转换为秒
times+=("$_t")
将秒添加到数组中
echo "${times[@]}" | sed 's/ /+/g' | bc
扩展每个参数并替换空格并将其通过管道传输到bc
一个通用的 linux 计算器
简化@jmunsch\的答案,并使用paste
我刚刚从@slm\的答案中学到的,你最终可能会得到这样的结果:
for i in *.ts; do LC_ALL=C ffmpeg -i "$i" 2>&1 | \\\nawk -F: \'/Duration:/{print $2*3600+$3*60+$4}\'; done | paste -sd+ | bc\n
Run Code Online (Sandbox Code Playgroud)\n\n就像 jmunsch 所做的那样,我用来ffmpeg
打印持续时间,忽略有关丢失输出文件的错误,而是在错误输出中搜索持续时间行。我将ffmpeg
语言环境的所有方面强制为标准 C 语言环境进行调用,这样我就不必担心本地化的输出消息。
接下来我将使用单个awk
而不是他的grep | grep | head | tr | awk
. 该awk
调用会查找包含 的(希望是唯一的)行Duration:
。使用冒号作为分隔符,该标签是字段 1,小时是字段 2,分钟是字段 3,秒是字段 4。秒后面的逗号似乎不会打扰我awk
,但如果有人在那里遇到问题,他可以在和tr -d ,
之间添加一个管道。ffmpeg
awk
现在来自 slm 的部分:我正在用paste
加号替换换行符,但不会影响尾随换行符(与tr \\\\n +
我在这个答案的先前版本中的相反)。这给出了可以输入到 的总和表达式bc
。
受到 slm 用于date
处理类似时间格式的想法的启发,这里有一个版本,它确实使用它来将结果秒格式化为带小数部分的天、小时、分钟和秒:
TZ=UTC+0 date +\'%j %T.%N\' --date=@$(for i in *.ts; do LC_ALL=C \\\nffmpeg -i "$i" 2>&1 | awk -F: \'/Duration:/{print $2*3600+$3*60+$4}\'; done \\\n| paste -sd+ | bc) | awk \'{print $1-1 "d",$2}\' | sed \'s/[.0]*$//\'\n
Run Code Online (Sandbox Code Playgroud)\n\n里面的部分$(\xe2\x80\xa6)
和以前一模一样。使用该@
字符作为指示,我们将其用作自 1970 年 1 月 1 日以来的秒数。生成的 \xe2\x80\x9cdate\xe2\x80\x9d 被格式化为一年中的日期、时间和纳秒。从一年中的这一天我们减去一,因为输入零秒已经导致 1970 年的第一天。我认为没有办法让一年中的某一天从零开始计数。
最后的结果sed
去掉了多余的尾随零。该TZ
设置有望强制使用 UTC,以便夏令时不会干扰真正的大型视频集合。不过,如果您有超过一年的视频,这种方法仍然行不通。