如何使用cp以幂等方式递归复制文件夹?


46

当我使用

cp -R inputFolder outputFolder

结果取决于上下文

  1. 如果outputFolder不存在,将创建它,并且克隆的文件夹路径为outputFolder
  2. 如果outputFolder存在,则创建的克隆将为outputFolder/inputFolder

这太可怕了,因为我想创建一些安装脚本,并且如果用户错误地运行了两次安装脚本,那么他将outputFolder是第一次创建该脚本,然后在第二次运行时,将在中再次创建所有内容outputFolder/inputFolder

  • 我总是想要第一个行为:在原始对象旁边(作为同级对象)创建一个副本。
  • 我想使用cp便携式(例如MINGW尚未rsync发货)
  • 我查了一下cp -R --parents,但这个重新创建的路径一路目录树(因此克隆也不会outputFolder,但是some/path/outputFolder
  • --remove-destination或者--update如果2不做任何更改,仍然将内容复制到outputFolder/inputFolder

有没有一种方法,而无需先检查是否存在outputFolder(如果文件夹不存在,则...)或使用rm -rf outputFolder

达成共识的可移植UNIX方式是什么?



2
其中一些选项不是POSIX,因此您尝试的选项不是非常可移植的。如果您可以忍受一点可移植性,为什么不使用rsync
muru 2015年

1
如果您不需要使用cp,则在两个tar命令之间进行管道传递是复制树的一种很好的可靠方法。
R..

@R ..您能举个例子吗?@muru我想在尚未rsync发货的MINGW中工作。
jakub.g 2015年

与GNU焦油一起使用tar -C input -cf - . | tar -C output -xf --C更改工作目录,但这不是标准选项,因此,如果不支持它,则需要在正确的工作目录中运行两个( cd input && tar -cf - . ) | ( cd output && tar -xf - )tars ,例如,但是,如果您使用的是Windows,我将使用roaima的回答。
R..

Answers:


54

使用此代替:

cp -R inputFolder/. outputFolder

例如,它的工作方式完全相同cp -R aaa/bbb ccc:如果ccc不存在,则将其创建为bbb及其内容的副本;但如果ccc已经存在,则将ccc/bbb作为bbb及其内容的副本创建。

对于bbb这种情况,几乎所有情况都会产生您在“问题”中指出的不良行为。但是,在这种特定情况下,bbbis,true .,true aaa/bbb,true aaa/.,而实际上又是aaa另一个名字。因此,我们有以下两种情况:

  1. ccc 不存在:

    该命令的cp -R aaa/. ccc意思是“创建ccc并将其内容复制aaa/.到中ccc/.,即复制aaa到中ccc

  2. ccc 确实存在:

    该命令的cp -R aaa/. ccc意思是“将的内容复制aaa/.到中ccc/.,即复制aaa到中ccc


3
呵呵,这对我来说是个新手,但是它确实起作用了!它记录在任何地方吗?
Ilmari Karonen 2015年

1
我已经测试过inputFolder/.代替,inputFolder并且确实可以在Windows / Git Bash上运行。(但是仅尾随它是行不通的/,在斜杠后也需要点)。我给+1是因为它很有趣,尽管在公共代码中使用它可能有点棘手:)
jakub.g 2015年

@IlmariJaronen不需要显式记录。按照说明进行操作,您将看到它如何简单地“遵循规则”。
roaima

18

不复制文件夹,仅复制内容:

## Create the target directory. The -p suppresses error messages
## if the directory already exists
mkdir -p outputFolder

## Copy the contents recursively, this will not recreate the parent
cp -R inputfolder/* outputfolder/

这样,既可以确保在脚本第一次运行时就创建了目标目录,又可以确保在第二次运行时避免该问题。

克里斯·唐纳(Chris Down)非常正确地指出,在bash中,这将跳过名称以a开头的文件.。为避免这种情况,您可以shopt -s dotglob在运行上面的命令之前运行。

无论-pmkdir-R用于cp通过POSIX定义所以这应该是非常便携。


6
注意:这不会复制以开头的文件.。在bash中,您可以使用dotglob它。
克里斯·

2
请注意,如果inputfolder中的目录条目数很大,则此方法可能会失败,因为glob扩展可能会超过最大命令行长度。
蒂姆·卡茨


4

您还可以使用rsync

rsync -uav inputFolder/ outputFolder/

(注意斜线,尤其是第一个斜线之后)


0

我认为,没有比这更简单,更便携的工具了rm -rf outputFolder。因此,我始终坚持这一点。我了解您的问题有所不同,但是我认为这是最佳做法。


如果目标上需要保留特定的权限或所有权,那将失败。或者,如果这是一个符号链接。
roaima 2015年

1
问题cp是目录不存在时和目录存在时都希望结果相同。那么,你在说什么呢?
dashohoxha 2015年

cpOP给出的命令不保留权限(或时间戳)。
roaima

0

您可以将命令-t选项cp用作:

cp -R inputFolder -t outputFolder

现在,如果目标文件夹不存在,它将引发错误:

cp: failed to access ‘outputFolder’: No such file or directory

注意,上面的命令将inputFolder与其内容一起复制(而不仅仅是其中的内容)

如果您只想复制它的内容,inputFolder会有些棘手(因为在使用星号*时必须小心使用shell globbing)

cp -R  -t outputFolder/ -- inputFolder/*

现在,如果目标文件夹不存在,它将引发错误:

cp: failed to access ‘outputFolder’: No such file or directory

与...合作 cp (GNU coreutils) 8.23

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.