在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设置以及它如何影响通配符行为。

更新于:2022年12月26日

861 次浏览

开启你的职业生涯

通过完成课程获得认证

开始学习
广告