在Linux系统中复制目录到已存在的目录?
概述
复制文件是使用Linux shell执行的最常见操作之一。我们通常为此目的使用cp(复制)命令。
我们将讨论如何递归地将文件夹移动到另一个位置,无论是否覆盖。
问题介绍
我们首先需要理解在这个问题中“将文件夹复制到另一个位置”是什么意思。
一个好的例子可以帮助你更好地理解它。
$ tree -a . ├── src │ ├── .hidden.file │ ├── srcFile.txt │ └── subSrc │ └── subSrcFile.txt └── target └── originalTarget.file
3个目录,4个文件
如上图所示,我们在src文件夹下有两个目录。有一些源代码文件和一个子文件夹。我们还有一个隐藏文件.hidden.file。
现在我们需要将源文件夹复制到目标文件夹。
我们希望目标文件夹与源文件夹相同。
├── src │ ├── .hidden.file │ ├── srcFile.txt │ └── subSrc │ └── subSrcFile.txt └── target ├── .hidden.file ├── srcFile.txt └── subSrc └── subSrcFile.txt
但是,如果我们只是将源文件夹的内容复制到目标文件夹,我们希望src下的所有内容都递归地移动到目标文件夹。我们也希望目标文件夹下的原始文件保持不变。
├── src │ ├── .hidden.file │ ├── srcFile.txt │ └── subSrc │ └── subSrcFile.txt └── target ├── .hidden.file ├── originalTarget.file ├── srcFile.txt └── subSrc └── subSrcFile.txt
由于目标目录已经存在,我们不能只使用cp -r src target方法来完成此任务。相反,我们需要首先在目标目录中创建一个名为src的新目录。然后,我们可以将文件从src移动到新创建的src目录。
之后,我们将讨论如何通过两种不同的方法来实现我们的目标:
使用cp命令
使用rsync命令
使用cp命令
让我们首先看看如何使用cp命令来解决这个问题,因为它相当常见。
cp命令不覆盖目标目录
如果源文件夹不为空,我们不能简单地使用cp命令将源文件夹的内容复制到目标文件夹。
但是,cp有一个-i选项,它将源文件视为普通文件而不是文件夹。
如果我们使用-rT选项,则cp命令将递归遍历源文件夹的内容,并在目标文件夹中创建它们的副本。
$ 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个目录,7个文件
tree的-a选项告诉我们我们正在将源目录中的所有内容复制到目标目录。但是,源目录不包含在复制操作中。所以我们完成了!
Bash通配符技巧
我们已经通过使用带有-rT选项的cp命令解决了这个问题。
我们可以通过将src中的所有内容复制到target来解决问题。因此,我们可能能够编写如下内容:cp -r src/* target。
我们可以通过将src中的所有内容复制到target来解决问题。因此,我们可能能够编写如下内容:cp -r src/* target。
$ cp -r src/* target $ tree -a . ├── src │ ├── .hidden.file │ ├── srcFile.txt │ └── subSrc │ └── subSrcFile.txt └── target ├── originalTarget.file ├── srcFile.txt └── subSrc └── subSrcFile.txt
4个目录,6个文件
正如tree输出所示,src下的大多数文件都成功传输到目标文件夹,除了.dot文件。这是因为使用“globstar”选项时,shell会忽略任何以句点(“.”)开头的文件名。
解决此问题的方法有两种。一种解决方案是修改全局设置,以便通配符包含.dot文件。为此,可以使用shopt -s dotglob命令。
$ ( shopt -s dotglob; cp -r src/* target ) $ tree -a target target ├── .hidden.file ├── originalTarget.file ├── srcFile.txt └── subSrc └── subSrcFile.txt
1个目录,4个文件
$ shopt dotglob
dotglob off
从上面的输出中我们可以看到,.dot文件也已成功创建。此外,我们将我们的shoptcp命令与cp命令一起放在括号(…)中,以便它们在子shell中运行,因为我们希望shoptcp命令只影响单个cp命令。
命令执行后,当我们检查通配符选项时,它们仍然被禁用。
更改bash的默认行为很容易。但是,如果您正在为自己编写脚本,这可能会很不方便。特别是,如果您想避免意外后果。
您也可以使用“src/.”而不是“src/*”将.dot文件添加到您的源代码控制系统。
让我们看看它是否按预期工作。
$ cp -r src/. target $ tree -a target target ├── .hidden.file ├── originalTarget.file ├── srcFile.txt └── subSrc └── subSrcFile.txt
1个目录,4个文件
cp命令和覆盖目标目录
如果我们想用目标文件夹的内容替换源文件夹的内容,那么使用-R选项比使用-O选项更容易。所以让我们首先删除目标文件夹并运行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个目录,6个文件
使用rsync命令
rsync是解决此问题的有用工具。
rsync命令不覆盖目标目录
我们可以使用两个rsync命令行来递归复制目录:
rsync -a source target:将source的内容复制到target
rsync -a src/ target - **将src目录的内容复制到target**
上面两个命令几乎相同。带有前导斜杠的命令正是我们正在寻找的。
让我们在我们的示例中测试一下:
$ 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个目录,7个文件
tree的结果显示该命令已解决我们的问题。
rsync命令和覆盖目标目录
我们已经了解到,我们可以组合两个命令行,“sudo rmdir /path/to/folder && sudo cp -r /source/folder /path/to/destination”来解决这个问题。
使用rsync,我们可以一次完成所有操作:
$ rsync -a --delete src/ target $ tree -a . ├── src │ ├── .hidden.file │ ├── srcFile.txt │ └── subSrc │ └── subSrcFile.txt └── target ├── .hidden.file ├── srcFile.txt └── subSrc └── subSrcFile.txt
4个目录,6个文件
执行rsync命令后,target和source都包含相同的内容。因此,我们找到了解决原始问题的替代方案。
此解决方案的秘诀是-delete开关。
-delete选项告诉rsync删除target/中不在source/中的任何文件,确保source和target在传输结束时最终相同。
结论
我们已经讨论了如何在递归地将文件夹移动到另一个文件夹,无论是否覆盖。
我们学习了两种解决问题的方法:使用常见的cp(复制)和rsync命令。
我们讨论了bash的dotglob设置以及它如何影响通配符行为。