cp

SYNOPSIS

    cp [OPTION]... [-T] SOURCE DEST
    cp [OPTION]... SOURCE... DIRECTORY
    cp [OPTION]... -t DIRECTORY SOURCE...

其实 cp 的选项比我想象中的要多很多。

其他选项:

  • 创建硬链接(-l)而不是拷贝。
  • -L 则会先解引用符号链接。
  • -n 不会覆盖旧文件
  • -a 先归档再拷贝,也就是保留所有属性。
  • -r 递归拷贝(文件夹)。
  • -u 只拷贝时间上更新的文件

在 shell 中合并两个目录可以用 rsync

rsync -auvh A/ B

au 选项 cp 也有,h 表示用人类可读单位显示体积,v 表示 verbose。注意:

  • 如果 A 后面没有斜杠,无论 B 是否存在,都会将 A 作为子文件夹拷贝进去
  • 如果用 A/*,则匹配不到隐藏文件。
  • B 后面有没有斜杠应该是不影响。
  • rsync 用于文件夹合并时,-r 选项是必须要加的,也就是至少需要 rsync -r A/ B。加 -a 也能满足这一点,因为 -a/--archive-rlptgoD 是等价的。(-r 表示递归,-l 表示以符号链接形式拷贝符号链接,-p 表示保留权限,-t 表示保留时间,-g 表示保留 group,-o 表示保留 owner,-D 等价于 --devices --specials,即保留块设备和特殊设备(FIFO、socket 等)。)

如何用 cp 来模拟这种行为

首先假设源文件 A 存在,B 可能不存在。B 如果存在且为普通文件则报错。

方案 1:使用 cp -r A B

❌ 不加 * 时,若目标文件夹存在,源文件夹本身被复制成为子文件夹。

$ rm -rf A B
$ mkdir -p A/1/2/3 B
$ cp -r A B
$ tree B
B
└── A
    └── 1
        └── 2
            └── 3

5 directories, 0 files

✔️ 若目标文件夹不存在,相当于使用 cp -r A B

$ rm -rf A B
$ mkdir -p A/1/2/3
$ cp -r A B
$ tree B
B
└── 1
    └── 2
        └── 3

4 directories, 0 files

方案 2:使用 cp -r A/* B

缺点:* 不能匹配上 Linux 的隐藏文件!

✔️ 若目标文件夹存在,工作正常。

$ rm -rf A B
$ mkdir -p A/1/2/3 B
$ cp -r A/* B
$ tree B
B
└── 1
    └── 2
        └── 3

4 directories, 0 files

❌ 若目标文件夹不存在,而源文件夹的子文件夹又恰好只有一个,则把子文件夹当成源文件夹拷贝。

$ rm -rf A B
$ mkdir -p A/1/2/3
$ cp -r A/* B
$ tree B
B
└── 2
    └── 3

3 directories, 0 files

❌ 如果目标文件夹不存在,且源文件夹的子文件夹有多个,则会失败:

$ rm -rf A B
$ mkdir -p A/1/2/3
$ mkdir -p A/4/2/3
$ cp -r A/* B
cp: target 'B': No such file or directory

加了 -t 选项之后会加上文件夹存在性检查,并在两种情况下都直接报错。

思考:所谓合并,就是目标文件夹可能存在,也可能不存在。存在时拷贝子文件夹,不存在时拷贝整个文件夹。

比较稳妥的模拟 rsync 效果的方法是?

find 的 + 模式并不会对每个输出都发起命令,而是将所有输出汇总后才发出。同时如果没有输出则会忽略这个命令,因而可以用来达到和 rsync 类似的效果。

mkdir -p B
find A -mindepth 1 -maxdepth 1 -exec cp -r -t B {} +

用 find 而不是 cp 的好处是也能找到隐藏文件。