You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下多层子目录中各类音频文件总时长统计问题

递归统计多层目录下音频/视频总时长问题

问题背景

我有一个包含mp3、ogg、opus、m4a等多种格式音频文件的大型多层目录,目录结构类似K/King, Stephen/The Stand/The Stand.mp3,需要统计所有音频文件的总时长。因为文件总大小达数百GB,无法复制到单一文件夹用mp3info处理;mp3info没有递归搜索选项,mp3info2虽有-R递归选项但资料不足不会用。

尝试过的脚本及报错

我尝试了以下脚本:

tot=0; while read -r i; do tmp=0;  tmp=`ffprobe "$i" -show_format 2>/dev/null | grep "^duration" | cut -d '=' -f 2 | cut -d '.' -f 1`; if [ -n "$tmp" ]; then let tot+=$tmp; fi;    done < <(find . -type f -iname "*[.mp3,.wav,.m3u,.m4a,.m4b,.mpga,.opus,.opus]"); echo "Total duration: $(($tot/60)) minutes"

出现重复报错:

bash: let: tot+=N/A: division by 0 (error token is "A")

另外尝试soxi -D *.mp3命令时,报错:

soxi FAIL formats: can't open input file `*.mp3': No such file or directory

额外问题

如何在另一目录中对视频文件执行同样的总时长统计操作?


解决方案

1. 修复音频统计脚本(解决N/A报错)

报错原因是部分文件ffprobe无法识别时长,返回N/A导致let命令无法计算,同时原find的后缀匹配规则错误,会匹配包含指定字符的任意文件而非目标格式。

修复后的脚本:

tot=0
while read -r file; do
    # 直接用ffprobe输出时长,简化处理
    duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file" 2>/dev/null)
    # 过滤有效数字,跳过无法识别的文件
    if [[ "$duration" =~ ^[0-9.]+$ ]]; then
        tot=$(( tot + $(printf "%.0f" "$duration") ))
    fi
done < <(find . -type f \( -iname "*.mp3" -o -iname "*.wav" -o -iname "*.m4a" -o -iname "*.m4b" -o -iname "*.mpga" -o -iname "*.opus" -o -iname "*.ogg" \))

# 转换为小时+分钟的直观格式
hours=$(( tot / 3600 ))
minutes=$(( (tot % 3600) / 60 ))
echo "总时长: $hours 小时 $minutes 分钟"

2. 解决soxi命令报错问题

soxi -D *.mp3报错是因为当前目录无mp3文件且soxi不支持递归,用以下命令递归统计:

find . -type f \( -iname "*.mp3" -o -iname "*.wav" -o -iname "*.m4a" -o -iname "*.opus" \) -exec soxi -D {} + | awk '{sum += $1} END {print "总时长(秒):", sum, "| 总时长(分钟):", sum/60, "| 总时长(小时):", sum/3600}'

3. 统计指定目录下的视频总时长

只需修改find的路径和视频格式后缀,示例如下:

tot=0
video_dir="/path/to/your/videos"
while read -r file; do
    duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file" 2>/dev/null)
    if [[ "$duration" =~ ^[0-9.]+$ ]]; then
        tot=$(( tot + $(printf "%.0f" "$duration") ))
    fi
done < <(find "$video_dir" -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.avi" -o -iname "*.mov" \))

hours=$(( tot / 3600 ))
minutes=$(( (tot % 3600) / 60 ))
seconds=$(( tot % 60 ))
echo "视频总时长: $hours 小时 $minutes 分钟 $seconds 秒"

内容的提问来源于stack exchange,提问作者ReaderGuy42

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 20:50:00