一键脚本
bash <(curl -sL gitee.com/meimolihan/cmdbox/raw/master/sh/move_mp4_cover.sh)
bash <(curl -sL ../sh/move_mp4_cover.sh)
效果预览
补充说明
扫描目录,把同目录下 xxx.mp4 单独新建同名文件夹,将 mp4 移入;同时查找同名 xxx.webp 封面一并移入;支持指定目录、遍历层级、优雅停止。
功能特点
- 自动归位:每个 xxx.mp4 自动新建同名文件夹 xxx/ 并移入,一一对应不冲突
- 封面跟随:在指定范围内查找同名 xxx.webp,找到则一并移入,找不到给出警告
- 指定目录:可传入起始目录,默认当前目录
- 遍历层级:交互或参数指定,仅一级子目录、或一层+二层嵌套子目录均可
- 优雅停止:任务运行时创建 ./.move.stop 标记文件,当前目录处理完毕后自动退出
- 交互确认:执行前展示工作目录与层级,确认后开始,可取消
- 逐目录自治:进入目录处理(子 shell),单目录失败不影响整体继续
用法示例
# 示例1:不带参数,当前目录运行,交互式选择层级
./move_mp4_cover.sh
# 示例2:指定目标目录,交互式选择层级
./move_mp4_cover.sh /vol2/1000/mydisk/Video/Western
# 示例3:指定目录 + 层级1(原版:仅一级子目录),直接开始
./move_mp4_cover.sh /vol2/1000/mydisk/Video/Western 1
# 示例4:当前目录 + 层级2(扫描一级+二级子目录),直接开始
./move_mp4_cover.sh "" 2
层级说明
- 层级1:只处理直接挂在 Western 下面的文件夹,文件夹里面的子文件夹忽略
- 层级2:Western 下一级文件夹 + 一级文件夹里面再嵌套一层文件夹,全部处理
优雅停止
任务执行过程中,在工作目录创建停止标记即可优雅退出:
touch ./.move.stop
脚本在进入每个目录前检查标记,发现后打印「🛑 检测到停止标记,任务退出」并自动删除标记,处理完当前目录后停止后续任务。
注意事项
- 需要 bash 4.0+(使用 ${变量,,} 小写转换判断确认输入)
- 第一个参数为空字符串(
"")时表示当前目录,便于与第二个参数配合使用 - 封面查找范围是整个起始目录树,重名时取第一个匹配(find 顺序)
- 只处理 *.mp4 且必然是文件;包含中文或空格的文件名安全支持
- 生成的同名文件夹与本脚本逻辑不冲突:重复运行已归位的 mp4 不再重复处理
- 建议先层级1小范围试跑,确认符合预期后再全量执行
脚本源码
#!/bin/bash
gl_hong='\033[0;31m'
gl_lv='\033[0;32m'
gl_huang='\033[1;33m'
gl_lan='\033[0;34m'
gl_hui='\033[1;30m'
gl_end='\033[0m'
STOP_FILE="./.move.stop"
move_mp4_and_cover() {
local SEARCH_ROOT="${1:-.}"
for f in ./*.mp4; do
[ -f "$f" ] || continue
base="${f%.mp4}"
base_name="$(basename -- "$base")"
target_dir="./$base_name"
mkdir -p "$target_dir"
mv -- "$f" "$target_dir/"
webp=$(find "$SEARCH_ROOT" -type f -name "${base_name}.webp" | head -n1)
if [ -n "$webp" ] && [ -f "$webp" ]; then
mv -- "$webp" "$target_dir/"
echo -e "${gl_lv}✅ 找到封面:$webp -> $target_dir/${gl_end}"
else
echo -e "${gl_huang}⚠️ 未找到 ${base_name}.webp${gl_end}"
fi
done
}
batch_run() {
local work_dir="$1"
local depth="$2"
local stop_file="${work_dir}/.move.stop"
echo -e "\n${gl_lan}===== 工作目录:${work_dir} | 遍历层级:maxdepth=${depth} =====${gl_end}"
find "${work_dir}" -mindepth 1 -maxdepth "${depth}" -type d -print0 | while IFS= read -r -d '' dir; do
if [[ -f "${stop_file}" ]]; then
echo -e "\n${gl_hong}🛑 检测到停止标记,任务退出${gl_end}"
rm -f "${stop_file}"
return 0
fi
echo -e "\n${gl_lan}===== 开始处理目录:$dir =====${gl_end}"
(
cd "$dir" || { echo -e "${gl_hong}❌ 无法进入 $dir${gl_end}"; exit 1; }
move_mp4_and_cover .
)
echo -e "${gl_lan}===== 目录 $dir 处理完成 =====${gl_end}\n"
done
echo -e "${gl_lv}✅ 全部目录处理完毕${gl_end}"
}
ARG_DIR="${1:-}"
ARG_DEPTH="${2:-}"
if [[ -z "${ARG_DIR}" ]]; then
WORK_DIR="$(pwd)"
else
WORK_DIR="${ARG_DIR}"
fi
WORK_DIR="$(realpath -- "${WORK_DIR}")"
if [[ -z "${ARG_DEPTH}" ]]; then
echo -e "${gl_huang}请选择遍历目录层级:${gl_end}"
echo -e "${gl_hui} 1 = 仅一级子目录(原版逻辑)${gl_end}"
echo -e "${gl_hui} 2 = 一级+二级子目录${gl_end}"
read -r -p "$(echo -e "${gl_huang}输入数字: ${gl_end}")" ARG_DEPTH
fi
if ! [[ "${ARG_DEPTH}" =~ ^[0-9]+$ ]]; then
echo -e "${gl_hong}❌ 层级必须是数字!${gl_end}"
exit 1
fi
echo -e "${gl_huang}工作目录: ${WORK_DIR}${gl_end}"
echo -e "${gl_huang}遍历maxdepth: ${ARG_DEPTH}${gl_end}"
read -r -p "$(echo -e "${gl_lv}确认开始?[Y/n] ${gl_end}")" confirm
confirm=${confirm:-Y}
if [[ ! "${confirm,,}" =~ ^y ]]; then
echo -e "${gl_huang}任务已取消${gl_end}"
exit 0
fi
batch_run "${WORK_DIR}" "${ARG_DEPTH}"
创建本地脚本
new_script="new_test.sh"
cat > "$new_script" << 'EOF'
#!/bin/bash
# 粘贴脚本源码
EOF
# 保留本地脚本,去掉 rm -f "$new_script"
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"
相关命令
- batch_mv_subdir 批量迁移
- file_rename_sorter 重命名整理
- fan-video_menu 媒体服务管理
- ffmpeg_video_info_list 视频信息
- move_mp4_cover 👈 当前所在位置