Linux命令cp文件夹时添加正斜杠和通配符的各种用法总结

终于把 cp 复制目录时的各种情况做了个总结。目录也是一种特殊的文件,但在复制时仍然有一些容易混淆的地方,尤其是源路径末尾的 /、通配符 *、以及目标目录是否已经存在。

下面用 [^] 表示空格。假设 /a 目录下有文件或子目录 123

cp[^]-R[^]/a/*[^]/b

等同于:

cp -R /a/* /b/

这会把 /a 目录下匹配 * 的内容复制到 /b 目录下。也就是说,复制的是 /a 里面的各个文件和子目录,而不是 /a 目录本身。

需要注意两点:

  1. * 由 shell 展开,默认不会匹配隐藏文件,例如 .gitignore.env.config 等。
  2. 如果 /a 下面有子目录,要递归复制它们,需要加 -R-r。在现代 GNU coreutils 里,-R-r 通常等价;如果关心可移植性,优先使用 POSIX 规定的 -R

如果想连隐藏文件也一起复制,可以考虑使用:

cp -R /a/. /b/

这里的 /a/. 表示复制 /a 的内容到 /b,同时包括普通文件、隐藏文件和子目录。

cp[^]-R[^]/a[^]/b/c

这种写法要分两种情况讨论:目标路径 /b/c 是否已经存在。

如果 /b/c 不存在

命令会创建 /b/c,并把 /a 中的所有文件和子目录复制到 /b/c 中。效果相当于把 /a 克隆为一个新的目录 /b/c

cp -R /a /b/c

复制后的结构类似:

/b/c/1
/b/c/2
/b/c/3

如果 /b/c 已经存在

命令会把 /a 目录本身复制到 /b/c 中。复制成功后,目录结构为:

/b/c/a/1
/b/c/a/2
/b/c/a/3

也就是说,目标目录已存在时,cp -R /a /b/c 的结果不是把 /a 的内容平铺到 /b/c,而是在 /b/c 下面再创建一个 a 目录。

cp[^]-R[^]/a/[^]/b

在 GNU/Linux 常见的 cp 实现中,源路径末尾的 / 通常不会改变复制目录时的主要行为。也就是说:

cp -R /a/ /b

通常与下面这条命令效果相近:

cp -R /a /b

如果 /b 已存在,复制后的结果一般是:

/b/a/1
/b/a/2
/b/a/3

如果没有加 -R-r,才会出现类似下面的错误:

cp: omitting directory '/a/'

cp[^]-R[^]/a/[^]/b/

目标路径末尾的 / 表示目标应该是一个目录。因此:

cp -R /a/ /b/

/b 已经存在时,通常会把 /a 目录复制到 /b 下面,结果为:

/b/a/1
/b/a/2
/b/a/3

如果 /b 不存在,不同系统和版本的报错信息可能略有差异。实际使用前,可以用下面的命令在本机验证行为:

mkdir -p /tmp/cp-test/a
printf 'one\n' > /tmp/cp-test/a/1
printf 'two\n' > /tmp/cp-test/a/2
printf 'three\n' > /tmp/cp-test/a/3
mkdir -p /tmp/cp-test/b
cp -R /tmp/cp-test/a/ /tmp/cp-test/b/
find /tmp/cp-test -maxdepth 3 -type f | sort

小结

常用结论如下:

cp -R /a/* /b/

复制 /a 里面被 * 匹配到的内容到 /b,默认不包括隐藏文件。

cp -R /a/. /b/

复制 /a 里面的全部内容到 /b,包括隐藏文件。

cp -R /a /b/c

如果 /b/c 不存在,把 /a 克隆成 /b/c;如果 /b/c 已存在,把 /a 复制成 /b/c/a

cp -R /a/ /b/

在常见 GNU/Linux 环境中,源路径末尾的 / 通常不会让 cp 只复制目录内容;要复制目录内容而不是目录本身,更明确的写法是 cp -R /a/. /b/

Leave a Reply