Zsh脚本递归重命名目录时误移动目录问题求助
问题分析
原脚本存在两个核心问题:
- 路径计算错误:
${folder%%[0-9]*}会截取路径中第一个数字前的部分,导致子目录被移动到父目录的上级目录(比如示例中的21 Finance被移到/Volumes/Files/Files/下),而非保留在原父目录内。 - 递归路径失效:目录重命名后,原路径
$folder已不存在,后续调用iterate_folders "$folder"会触发“no matches found”错误。
修复后的脚本
以下是修正后的Zsh脚本,可正确匹配并移除目录名开头的两位整数、小数(含小数点)及连带分隔符(如空格、连字符):
#!/bin/zsh # 移除目录名开头的Johnny Decimals前缀 remove_johnny_decimal() { local folder="$1" local parent_dir=$(dirname "$folder") local old_name=$(basename "$folder") # 匹配开头的数字、小数点、连字符,以及后续的空格,替换为空 local new_name=${old_name/#[0-9.-]+[[:space:]]*/} # 仅当名称有变化时执行重命名 if [[ "$old_name" != "$new_name" ]]; then local new_folder="$parent_dir/$new_name" mv -v "$folder" "$new_folder" # 返回新路径供递归调用 echo "$new_folder" else # 名称无需修改,返回原路径 echo "$folder" fi } # 递归遍历目录:先处理子目录,再处理当前目录(避免父目录重命名影响子路径) iterate_folders() { local dir="$1" # 先收集所有子目录,避免遍历过程中路径变化导致的问题 local subdirs=() for item in "$dir"/*; do if [[ -d "$item" ]]; then subdirs+=("$item") fi done # 递归处理每个子目录 for subdir in "${subdirs[@]}"; do local new_subdir=$(remove_johnny_decimal "$subdir") iterate_folders "$new_subdir" done # 最后处理当前目录本身 remove_johnny_decimal "$dir" } # 参数检查 if [[ $# -ne 1 ]]; then echo "用法: $0 <主目录路径>" exit 1 fi main_folder="$1" if [[ ! -d "$main_folder" ]]; then echo "错误: 主目录不存在: $main_folder" exit 1 fi # 启动递归处理 iterate_folders "$main_folder"
关键改进点
- 仅修改目录名部分:拆分路径为父目录和basename,只对basename做正则替换,确保子目录保留在原父目录内。
- 精准正则匹配:使用
${old_name/#[0-9.-]+[[:space:]]*/}匹配开头的数字、小数点、连字符及后续空格(比如20-29 Abstract...会被替换为Abstract...)。 - 先子后父的递归顺序:先处理所有子目录,再处理当前目录,避免父目录重命名后子路径失效。
- 返回新路径:重命名后返回新路径,确保递归调用使用正确的路径。
替代方案(使用find命令)
如果更倾向于用find工具,可使用以下方式(建议先加-print查看效果再执行重命名):
# 从最深层目录开始处理,避免父目录重命名影响子路径 find "$1" -type d -print0 | sort -rz | while IFS= read -r -d '' dir; do parent=$(dirname "$dir") old_name=$(basename "$dir") new_name=${old_name/#[0-9.-]+[[:space:]]*/} if [[ "$old_name" != "$new_name" ]]; then mv -v "$dir" "$parent/$new_name" fi done
内容的提问来源于stack exchange,提问作者Kamil
相关产品推荐
相关产品推荐

