如何在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/中的文件。通过这种方式,它确保src和target最终相同。**