Contents

如何在Linux中将目录复制到现有目录

1. 概述

当我们使用 Linux 命令行时,文件复制是一种常见的文件操作。通常,我们选择cp  命令来复制文件。

在本教程中,我们将讨论如何以递归方式将目录复制到现有目录,无论是否覆盖。

2. 问题介绍

首先,我们需要了解“将目录复制到现有目录”在这个问题中的确切含义。

一个例子可以清楚地解释它:

$ tree -a
.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    └── originalTarget.file
3 directories, 4 files

在这里,如上面的输出所示,我们有两个目录。在 src目录下,有一些文件和一个子目录。此外,我们还有一个点文件*.hidden.file*。

现在,我们要将src目录复制到target目录。

如果我们用覆盖复制,在复制操作之后,我们希望 target目录包含与src目录完全相同的内容:

.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

但是,如果我们复制而不覆盖,我们希望src目录下的所有内容都递归复制到 target目录。此外,target下的原始文件必须保持不变:

.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

由于 target目录已经存在,我们不能简单地使用命令cp -r src target来完成这项工作。那是因为该命令会将target下的整个src目录复制为子目录: /target/src/。这不是我们真正想要的。

接下来,我们将讨论如何使用两种不同的方法来实现我们的目标:

  • 使用cp命令
  • 使用 rsync 命令

3. 使用cp命令

我们将首先看看如何使用cp命令解决问题,因为它很常见。

3.1. cp不覆盖target目录

我们理解,如果target是现有目录,我们不能使用简单的命令cp -r src target来解决这个问题。

但是,cp提供了一个不错的-T*选项来将目标视为普通文件而不是目录*。

也就是说,如果我们结合*-rT选项,cp命令会递归地将src目录下的内容复制到target*目录:

$ cp -rT src target
$ tree -a
.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
4 directories, 7 files

tree的输出显示我们已将src目录下的所有内容(包括 dotfile)复制到target目录。并且 src目录本身没有被复制。这样,我们就解决了这个问题。

3.2. Bash 通配技巧

我们已经使用 带有*-rT选项的cp*命令解决了这个问题。

如果我们重新考虑问题,要求只是复制src 下的内容。因此,我们可能会想出命令cp -r src/* target。

事实上,这是解决问题的另一种可能性。但是,命令中有一个小缺陷。首先,让我们清理target目录并试一试,看看会发生什么:

$ cp -r src/* target
$ tree -a
.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
4 directories, 6 files

如 tree输出所示,  src下的几乎所有内容都已复制到 target目录,除了点文件。这是因为Bash 中的默认通配符不包括以点 (“.”) 开头的文件名

我们有两种方法来解决它。一种方法是更改默认设置并使通配符包含点文件。我们可以使用shopt -s dotglob命令来实现:

$ ( shopt -s dotglob; cp -r src/* target )
$ tree -a target
target
├── .hidden.file
├── originalTarget.file
├── srcFile.txt
└── subSrc
    └── subSrcFile.txt
1 directory, 4 files
$ shopt dotglob
dotglob        off

正如我们在上面的输出中看到的,点文件也被复制了。此外,我们将shopt命令与 cp命令一起包装在“ (….)”中,以使它们在子 shell 中运行,因为我们希望shopt命令只影响单个cp命令。

因此,在命令执行后,当我们检查 dotglob选项时,它仍然是禁用的。

更改 Bash 的默认行为有效。但是,它不是那么方便。特别是,当我们编写一个 shell 脚本时,它会带来副作用。

包含点文件的另一种方法是使用“ src/. ” 而不是“ src/* ”,因为“ src/. ” 不是一个通俗的表达方式。这意味着src目录下的所有内容。

让我们看看它是否像我们预期的那样工作:

$ cp -r src/. target
$ tree -a target
target
├── .hidden.file
├── originalTarget.file
├── srcFile.txt
└── subSrc
    └── subSrcFile.txt
1 directory, 4 files

很好,我们得到了预期的结果。

3.3. cp并覆盖target目录

如果我们想复制src并覆盖target目录,这比非覆盖变体更容易。我们可以先删除target目录,然后应用 cp -r命令解决问题:

$ rm -r target && cp -r src target
$ tree -a
.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
4 directories, 6 files

4. 使用 rsync命令

rsync是一个强大的文件复制工具。它可以帮助我们轻松解决这个问题。

4.1. rsync不覆盖target目录

两个 rysnc命令可以帮助我们递归地复制目录:

  • rsync -a src target : 将目录src及其内容复制到target
  • rsync -a src/ target : 将src目录的内容复制到targe t

上面的两个命令看起来非常相似。带有斜线的那个正是我们正在寻找的。

让我们在我们的示例中对其进行测试:

$ rsync -a src/ target
$ tree -a
.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
4 directories, 7 file

tree的输出显示该命令已经解决了我们的问题。

4.2. rysnc 并覆盖target目录

我们知道我们可以结合两个命令,“ rm -r target && cp -r src target ”来解决这个问题。

如果我们拥有 rysnc,我们可以一口气实现它:

$ rsync -a --delete src/ target
$ tree -a
.
├── src
│   ├── .hidden.file
│   ├── srcFile.txt
│   └── subSrc
│       └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
4 directories, 6 files

如输出所示,在我们执行rsync命令后,target和 src包含相同的内容。所以,我们用另一种方式解决了这个问题。

此解决方案的关键是选项 –delete

** –delete选项告诉rysnc从 target/ 中删除不在src/中的文件。通过这种方式,它确保srctarget最终相同。**