如何修改Bash脚本实现仅分割M4B文件的第2至第6章节
你的脚本出现语法错误的核心原因是拿到的chapter变量是章节名称(比如"Opening Credits")而不是章节数字,导致数值比较((chapter >=2 && chapter <=6))无法执行。下面我会帮你修正脚本的核心问题,同时优化循环逻辑避免重复处理文件。
关键问题分析
原来的脚本中,read -r _ _ chapter这一行读取的是章节的名称而非数字,当你用数值比较符判断字符串时,Bash就会抛出syntax error。另外,内层的for f in *.m4b循环和外层的参数处理循环重复,会导致文件被多次处理。
修改后的完整脚本
#!/bin/bash # 仅分割M4B文件的第2至第6章节,输出为MP3 # 运行方式: bash m4b-split.sh *.m4b currentdir=$(pwd) # 获取当前目录 while [ $# -gt 0 ]; do f="$1" # 跳过非M4B文件 if [[ "$f" != *.m4b ]]; then echo "跳过非M4B文件: $f" shift continue fi fn=$(basename "$f" | cut -d'.' -f1) # 获取不带扩展名的文件名 echo "正在处理文件: ($fn)" splitdirname="$currentdir/final-split-$fn" # 生成存储分割文件的子目录 mkdir -p "$splitdirname" # 创建目录(如果不存在) # 用ffmpeg提取章节信息到临时文件 ffmpeg -i "$f" 2> tmp.txt # 逐行解析临时文件中的章节信息 while read -r line; do if [[ "$line" == Chapter* ]]; then # 从Chapter行提取章节数字(比如从"Chapter #0:0: start ..."中提取0) chapter_num=$(echo "$line" | sed -E 's/Chapter #([0-9]+):.*/\1/') # 提取开始和结束时间 start=$(echo "$line" | awk '{print $4}') end=$(echo "$line" | awk '{print $6}' | sed 's/,$//') # 去掉end后面的逗号 # 跳过Metadata行 read -r # 读取章节名称 read -r _ _ chapter_name # 仅处理第2至第6章 if (( chapter_num >= 2 && chapter_num <= 6 )); then # 生成输出文件名(替换空格为短横线避免问题) output_file="$splitdirname/${chapter_num}-${chapter_name// /-}-${fn}.mp3" echo "正在分割章节 $chapter_num: $chapter_name -> $output_file" # 调用ffmpeg执行分割和转码 ffmpeg -vsync 2 -i "$f" -ss "$start" -to "$end" \ -metadata title="${chapter_num}. ${chapter_name}" \ -metadata track="$chapter_num" \ -vn -ar 22050 -ac 1 -ab 128k -f mp3 "$output_file" < /dev/null fi fi done < tmp.txt rm tmp.txt # 清理临时文件 shift # 处理下一个传入的文件 done
核心修改点说明
修复循环逻辑
去掉了多余的内层for f in *.m4b循环,直接处理你传入的每个参数文件,同时增加了非M4B文件的跳过逻辑,避免无效处理。正确提取章节数字
通过sed -E 's/Chapter #([0-9]+):.*/\1/'从Chapter行本身提取章节编号,确保chapter_num是纯数字,这样数值比较((chapter_num >=2 && chapter_num <=6))就能正常执行。优化时间参数处理
直接从Chapter行提取start和end时间,并通过sed 's/,$//'去掉end后面的逗号,避免ffmpeg解析时间参数时报错。文件名和元数据优化
保留章节名称用于输出文件名和MP3的标题元数据,同时把名称中的空格替换为短横线,避免文件名出现空格引发的问题。临时文件清理
确保每次处理完一个文件后都删除临时文件tmp.txt,避免残留数据影响后续处理。
现在运行bash m4b-split.sh *.m4b就可以只分割每个M4B文件的第2到第6章节了。
备注:内容来源于stack exchange,提问作者Rick T

