小易说IT 小易说IT

Shell 脚本中高效使用 cp 的要点

核心原则:减少不必要拷贝、避免误覆盖、善用参数、批量优先用 glob/xargs/rsync、大文件注意缓存

一、常用参数(脚本里最实用)

# -n:不覆盖已存在文件(脚本安全首选,避免误删数据)
cp -n src.txt dest/

# -u:只在源更新(源比目标新 / 目标不存在)才拷贝,增量拷贝
cp -u src.txt dest/

# -a:归档模式,递归、保留权限/属主/时间、软链接不拆(备份目录首选)
# -a = -dR --preserve=all
cp -a src_dir/ dest_dir/

# -R / -r 递归拷贝目录;-R 会保留符号链接,-r 部分老实现会跟随链接
cp -R src_dir dest_dir

# -v 显示过程(调试脚本用,正式脚本建议去掉减少输出)
cp -v src dest

# -f 强制覆盖(慎用!脚本里尽量不要默认加 -f,高危)

⚠️ 重点区分:

  • cp -a src_dir dest_dir:把 src_dir 整个放进 dest_dir 下面

  • cp -a src_dir/ dest_dir:把 src_dir/**里面的内容** 拷贝到 dest_dir(末尾斜杠很关键)

二、批量拷贝:不要写循环 for 逐个 cp(低效)

❌ 不推荐(多次 fork cp 进程,大量文件时很慢)

for f in *.log; do cp "$f" bak/; done

✅ 推荐 glob 一次传给 cp,单进程:

cp -n -- *.log bak/

-- 作用:防止文件名以 - 开头被当成命令参数(脚本健壮性必备)

如果文件太多,glob 报 argument list too long(参数过长),改用 xargs:

# 安全写法,带-n,处理空格文件名
find . -maxdepth 1 -type f -name "*.log" -print0 | xargs -0 cp -n -t bak/

-t bak/:指定目标目录,xargs 配合 cp 非常好用。

三、安全写法(脚本必做,防止文件名含空格、换行、特殊字符)

所有变量包裹双引号 "$var"

src="my file.txt"
dest_dir="./target"
cp -n -- "$src" "$dest_dir/"

不加引号遇到 a b.txt 直接炸;-- 拦截 -badname.txt

四、增量备份场景:cp -u vs rsync

cp -u 简单轻量,但有短板:只比对 mtime + 大小;不校验内容;不能远程、不支持删除目标多余文件。

  • 简单本地增量、不想依赖 rsync:cp -au

  • 大量文件、需要校验、同步删除、远程:优先 rsync(比循环 cp 高效得多)

rsync -au --delete src_dir/ dest_dir/

五、大文件拷贝优化

  1. 连续多次 cp 大文件,Linux page cache 会占内存;脚本里如果需要释放缓存(测试场景):

# 非必要不要随便用,需要root
sync; echo 3 > /proc/sys/vm/drop_caches
  1. 如果是重复拷贝同一个大文件,可以考虑 dd、fallocate 或者 reflink(Btrfs/XFS 支持,零拷贝!)

# btrfs/xfs 同分区下,reflink 几乎瞬间完成,不占用额外空间
cp --reflink=always bigfile.img bigfile-clone.img

注意:跨分区不支持 reflink,会自动回退普通 copy。判断支持性可以捕获返回码。

六、错误处理(脚本里非常重要)

cp 失败返回非 0,配合 set -e 或者手动判断:

#!/bin/bash
set -euo pipefail

if ! cp -n -- "$src" "$dst"; then
    echo "拷贝失败:$src" >&2
    exit 1
fi
  • set -e:命令失败直接退出脚本

  • set -u:未定义变量直接报错(防止空变量导致 cp / xxx 灾难)

七、常见坑清单

  1. 不要默认写 cp -rf:无提示覆盖,脚本跑崩数据就没了

  2. 目录拷贝末尾 / 差异,是高频踩坑点

  3. 文件名以 - 开头:必须加 --

  4. 大量小文件:cp glob > xargs cp > for 循环逐个 cp

  5. 软链接:cp -a 保留链接;不加 -a 默认拷贝链接指向的真实文件

  6. 权限:普通用户无法保留属主,cp -a 在跨用户拷贝时会丢掉 owner

八、模板:一个健壮高效的批量拷贝脚本片段

#!/bin/bash
set -euo pipefail

SRC_DIR="./source"
DST_DIR="./backup"
mkdir -p -- "$DST_DIR"

# 方式1:文件数量不多,glob
cp -au -- "$SRC_DIR"/*.txt "$DST_DIR"/

# 方式2:文件超多,防参数过长,兼容空格文件名
find "$SRC_DIR" -maxdepth 1 -type f -name "*.csv" -print0 |
  xargs -0 cp -au -t "$DST_DIR"

九、什么时候不该用 cp?

  • 需要同步、删除目标多余文件 → rsync

  • 跨机器拷贝 → scp /rsync

  • 同文件系统大量快照克隆 → reflink cp(btrfs/xfs)

  • 并行批量拷贝大量文件 → 用 GNU parallel 而不是循环