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

Zsh脚本递归重命名目录时误移动目录问题求助

问题分析

原脚本存在两个核心问题:

  1. 路径计算错误:${folder%%[0-9]*}会截取路径中第一个数字前的部分,导致子目录被移动到父目录的上级目录(比如示例中的21 Finance被移到/Volumes/Files/Files/下),而非保留在原父目录内。
  2. 递归路径失效:目录重命名后,原路径$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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 03:02:40