解决zsh嵌套for循环处理视频合并时的‘no matches found’错误
解决zsh中无匹配文件时循环中断的问题,同时优化FFmpeg合并逻辑
你遇到的核心问题是zsh的默认行为导致的:当通配符(比如ch1*.mp4)没有匹配到任何文件时,zsh会直接抛出no matches found错误并中断整个循环,而不是像bash那样跳过无匹配的情况。另外,你的FFmpeg合并命令也有小瑕疵——直接用-i $VIDEOLIST会把多个文件作为独立输入流,最终输出的视频可能包含多轨而非连续的单轨视频,这里也一起帮你修正。
下面是修正后的完整脚本,附带详细解释:
# 开启nullglob选项:当通配符无匹配时,展开为空字符串而非报错 setopt nullglob # 遍历所有日期文件夹(*/会匹配所有子目录,nullglob确保无目录时不执行循环) for VIDEODIR in */; do # 提取日期:去掉目录末尾的斜杠,比如把"2021-07-03/"变成"2021-07-03" DATE="${VIDEODIR%/}" # 遍历每个摄像头通道 for VIDEOCHANNELNUMBER in 1 2 3 4 5 6; do # 收集当前日期+通道的所有视频文件,按文件名排序(确保时间顺序正确) VIDEO_FILES=("$VIDEODIR/ch${VIDEOCHANNELNUMBER}"*.mp4) # 如果没有找到任何文件,跳过当前通道 if [[ ${#VIDEO_FILES[@]} -eq 0 ]]; then echo "No files found for channel $VIDEOCHANNELNUMBER on $DATE, skipping..." continue fi # 生成FFmpeg需要的文件列表(每个文件路径前加file:) LIST_FILE="/tmp/concat_${DATE}_ch${VIDEOCHANNELNUMBER}.txt" > "$LIST_FILE" # 清空或创建文件 for f in "${VIDEO_FILES[@]}"; do echo "file '$PWD/$f'" >> "$LIST_FILE" done # 使用FFmpeg的concat demuxer合并视频(同编码下-c copy保证无损快速合并) OUTPUT_FILE="${DATE}-CH${VIDEOCHANNELNUMBER}.mp4" ffmpeg -f concat -safe 0 -i "$LIST_FILE" -c copy "$OUTPUT_FILE" # 检查合并是否成功,成功则删除原目录 if [[ $? -eq 0 ]]; then echo "Successfully merged $OUTPUT_FILE, deleting source directory $VIDEODIR..." rm -r "$VIDEODIR" rm "$LIST_FILE" # 清理临时文件 else echo "Failed to merge $OUTPUT_FILE, keeping source files." rm "$LIST_FILE" fi done done
关键修正点说明:
setopt nullglob:这是解决你核心问题的关键——开启后,当通配符没有匹配到任何文件时,会展开成空字符串,而不是抛出错误中断循环。- 数组存储视频文件:用
VIDEO_FILES=("$VIDEODIR/ch${VIDEOCHANNELNUMBER}"*.mp4)把匹配到的文件存入数组,方便后续判断是否有文件,且数组默认按文件名排序(你的文件名包含时间戳,刚好符合合并顺序要求)。 - FFmpeg concat demuxer:相比直接用多个
-i参数,这种方式更可靠,能确保视频按顺序合并成连续的单轨视频,-safe 0允许使用绝对路径,避免FFmpeg报错。 - 跳过空通道:通过
${#VIDEO_FILES[@]} -eq 0判断是否有文件,没有则直接continue跳过当前通道,继续处理下一个。
其他可选方案(不用nullglob):
如果你不想修改zsh的全局选项,也可以用find命令来查找文件,完全避免通配符匹配问题:
for VIDEODIR in */; do DATE="${VIDEODIR%/}" for VIDEOCHANNELNUMBER in 1 2 3 4 5 6; do # 用find查找并按文件名排序 VIDEO_FILES=($(find "$VIDEODIR" -maxdepth 1 -name "ch${VIDEOCHANNELNUMBER}*.mp4" -type f | sort)) if [[ ${#VIDEO_FILES[@]} -eq 0 ]]; then echo "No files found for channel $VIDEOCHANNELNUMBER on $DATE, skipping..." continue fi # 后续合并逻辑和上面的脚本一致 done done
内容的提问来源于stack exchange,提问作者Mr. Demetrius Michael
相关产品推荐
相关产品推荐

