个人工具 · 文件整理

图片批量剪切工具

把文件夹 A(含所有子文件夹)里的图片,统一剪切到文件夹 B。这页只负责生成你要跑的那条终端命令——真正的剪切在你自己的 Mac 本地执行,路径和文件都不会经过网络。

支持格式
jpg/png/gif/webp/heic/svg/raw 等
重名冲突
自动加 _1 _2 后缀,不覆盖
子文件夹
全部递归找,不管多少层
执行位置
本地终端,不上传任何文件

一、先把脚本装到本地(只用做一次)

这是个本地脚本,真正的剪切靠它完成。已经装过的话可以跳过这步——重新跑一遍也没关系,会覆盖成最新版本。

  1. 打开终端
    ⌘ 空格,输入「终端」或「Terminal」,回车。
  2. 把下面这一整块粘进去,回车
    会在 ~/bin/ 下建好 move-images.sh 并加上执行权限。
    mkdir -p ~/bin
    cat > ~/bin/move-images.sh <<'EOF'
    #!/bin/bash
    # 把文件夹 A(含所有子文件夹)里的图片全部剪切到文件夹 B
    # 用法:
    #   ./move-images.sh                      # 交互模式,按提示粘贴两个路径
    #   ./move-images.sh /path/A /path/B      # 直接模式
    
    set -euo pipefail
    
    IMG_EXTS=(jpg jpeg png gif bmp tiff tif webp heic heif svg ico raw cr2 nef arw dng psd)
    
    # 去掉路径首尾的空白和引号(兼容手动加引号粘贴的情况)
    clean_path() {
        local p="$1"
        p="${p#"${p%%[![:space:]]*}"}"
        p="${p%"${p##*[![:space:]]}"}"
        p="${p%\"}"; p="${p#\"}"
        p="${p%\'}"; p="${p#\'}"
        printf '%s' "$p"
    }
    
    if [[ $# -ge 2 ]]; then
        SRC_RAW="$1"
        DST_RAW="$2"
    else
        # -e 让 read 支持反斜杠转义,兼容从访达拖拽文件夹时自动加的 "\ " 转义空格
        read -e -p "请粘贴原始文件夹 A 的路径: " SRC_RAW
        read -e -p "请粘贴目标文件夹 B 的路径: " DST_RAW
    fi
    
    SRC="$(clean_path "$SRC_RAW")"
    DST="$(clean_path "$DST_RAW")"
    SRC="${SRC/#\~/$HOME}"
    DST="${DST/#\~/$HOME}"
    
    if [[ ! -d "$SRC" ]]; then
        echo "错误:源文件夹不存在 → $SRC" >&2
        exit 1
    fi
    
    SRC_ABS="$(cd "$SRC" && pwd)"
    mkdir -p "$DST"
    DST_ABS="$(cd "$DST" && pwd)"
    
    if [[ "$SRC_ABS" == "$DST_ABS" ]]; then
        echo "错误:A 和 B 是同一个文件夹" >&2
        exit 1
    fi
    
    # 拼出 find 的 -iname 匹配条件
    FIND_EXPR=()
    for ext in "${IMG_EXTS[@]}"; do
        [[ ${#FIND_EXPR[@]} -gt 0 ]] && FIND_EXPR+=(-o)
        FIND_EXPR+=(-iname "*.${ext}")
    done
    
    # 如果 B 就在 A 里面,搜索时要把 B 排除掉,避免把刚搬进去的文件又搬一遍
    PRUNE_EXPR=()
    if [[ "$DST_ABS" == "$SRC_ABS"/* ]]; then
        PRUNE_EXPR=(-path "$DST_ABS" -prune -o)
    fi
    
    count=0
    dup=0
    
    while IFS= read -r -d '' f; do
        base="$(basename "$f")"
        target="$DST_ABS/$base"
        if [[ -e "$target" ]]; then
            name="${base%.*}"
            ext="${base##*.}"
            i=1
            while [[ -e "$DST_ABS/${name}_${i}.${ext}" ]]; do
                ((i++))
            done
            target="$DST_ABS/${name}_${i}.${ext}"
            ((dup++))
        fi
        mv "$f" "$target"
        ((count++))
    done < <(find "$SRC_ABS" "${PRUNE_EXPR[@]+"${PRUNE_EXPR[@]}"}" -type f \( "${FIND_EXPR[@]}" \) -print0)
    
    echo "完成:共剪切 $count 张图片到 $DST_ABS"
    [[ $dup -gt 0 ]] && echo "其中 $dup 张因重名已自动加序号后缀"
    EOF
    chmod +x ~/bin/move-images.sh

二、生成剪切命令

把 A、B 两个文件夹的路径填进去,点生成。

怎么拿到文件夹的完整路径

在访达里选中文件夹,按住 ⌥ Option 键,右键菜单会出现「拷贝"XX"的路径名」,点一下就复制好了完整路径,直接粘贴到下面输入框。

三、粘贴到终端跑

  1. 回到终端窗口,粘贴刚生成的命令,回车
  2. 看输出
    跑完会打印「完成:共剪切 N 张图片到 ……」;如果有重名文件,还会额外提示有几张加了序号后缀。
第一次跑可能弹权限框

如果 A 或 B 在「桌面」「文稿」「下载」这类系统保护目录下,macOS 第一次会弹窗问「终端」要不要访问这个文件夹,点允许就行——不点的话文件挪不动。

四、这条命令的行为