如何强制“ cp”覆盖目录,而不是在内部创建另一个目录?

我正在尝试编写一个 Bash 脚本,它将覆盖现有的目录。我有一个目录 foo/,我试图用它覆盖 bar/。但是当我这么做的时候:

cp -Rf foo/ bar/

创建一个新的 bar/foo/目录。我不想那样。在 foo/中有两个文件: ab。在 bar/中也有同名的文件。我想用 foo/afoo/b代替 bar/abar/b

218210 次浏览

使用这个 cp命令:

cp -Rf foo/* bar/

分两步做。

rm -r bar/
cp -r foo/ bar/

您可以在 cp中使用 -T选项来完成此操作。
参见 cp的手册页。

-T, --no-target-directory
treat DEST as a normal file

因此,根据您的示例,下面是文件结构。

$ tree test
test
|-- bar
|   |-- a
|   `-- b
`-- foo
|-- a
`-- b
2 directories, 4 files

在对详细信息使用 -v时,您可以看到明显的区别。
当您只使用 -R选项时。

$ cp -Rv foo/ bar/
`foo/' -> `bar/foo'
`foo/b' -> `bar/foo/b'
`foo/a' -> `bar/foo/a'
$ tree
|-- bar
|   |-- a
|   |-- b
|   `-- foo
|       |-- a
|       `-- b
`-- foo
|-- a
`-- b
3 directories, 6 files

当您使用选项 -T时,它会覆盖内容 像对待普通文件而不是目录一样对待目标

$ cp -TRv foo/ bar/
`foo/b' -> `bar/b'
`foo/a' -> `bar/a'


$ tree
|-- bar
|   |-- a
|   `-- b
`-- foo
|-- a
`-- b
2 directories, 4 files

这应该能解决你的问题。

下面的命令确保 dotfiles (隐藏文件)包含在副本中:

$ cp -Rf foo/. bar

与@Jonathan Wheeler 非常相似:

如果你不想记住,但不重写 bar:

rm -r bar/
cp -r foo/ !$

!$显示上一个命令的最后一个参数。

如果您想确保 bar/最终与 foo/完全相同,请使用 rsync:

rsync -a --delete foo/ bar/

如果只有少数内容发生了更改,执行速度将远远快于删除和重新复制整个目录。

  • -a是“归档模式”,它忠实地将 foo/中的文件复制到 bar/
  • --deletebar/中删除了不在 foo/中的额外文件,以确保 bar/最终完全相同
  • 如果您想看看它在做什么,那么添加 -vh以获得冗长且人类可读的内容
  • 注意: foo后面的斜杠是必需的,否则 rsync将把 foo/复制到 bar/foo/,而不是覆盖 bar/本身。
    • (rsync 中目录后面的斜杠会让人感到困惑; 如果您感兴趣,这里有独家新闻。它们告诉 rsync 引用目录的 内容,而不是目录 本身。因此,为了将 foo/内容覆盖到 bar/的内容上,我们对两者都使用斜杠。令人困惑的是,在 bar/0上使用斜杠并不能像预期的那样工作; rsync 总是偷偷地将目标路径解释为使用斜杠,即使它在源路径上没有使用斜杠。因此,如果我们想要将 foo/内容复制到 bar/中,而不是将目录 foo/本身作为 bar/foo复制到 bar/中,那么我们需要在源路径上使用一个斜杠,使其与目标路径上自动添加的斜杠相匹配。)

rsync非常强大和有用,如果您对它的其他功能感到好奇,可以查看它的其他功能(比如通过 ssh 进行复制)。

您定义的操作是一个“ merge”,您不能对 cp执行该操作。但是,如果你不寻找合并和确定丢失的文件夹 bar,那么你可以简单地 rm -rf bar删除文件夹,然后 mv foo bar重命名它。这不会花费任何时间,因为这两个操作都是由文件指针完成的,而不是文件内容。

尝试使用这个由两个步骤组成的命令:

cp -Rf foo/* bar/

这应该能解决你的问题。

\cp -rf foo/* bar/

如果你在 用于 Windows 的 Git Bash:

robocopy path/to/source path/to/target -MIR

-MIR意味着镜像,它将导致两个相同的文件夹。现有的文件将被跳过,额外的文件将被删除。