复制没有文件的目录结构
1. 概述
我们知道我们可以使用cp 命令和 -R选项递归地复制目录 。这样,源目录及其下的所有文件将被复制到目标目录。
有时,我们不想从源目录中复制所有文件。相反,我们只想将空目录结构复制到目标目录。
在本教程中,我们将解决如何实现这一点。
2. 问题介绍
为了清楚地理解问题,让我们看一个目录示例rootDir:
$ tree -a rootDir
rootDir
├── dir1
│ ├── 1_level.file
│ └── dir1_1
│ └── 1_1_level.file
├── dir2
│ └── 2_level.file
├── dir3
│ └── dir3_1
│ ├── 3_1_level.file
│ └── dir3_1_1
│ └── 3_1_1_level.file
└── root_level.file
6 directories, 6 files
在rootDir目录中,我们有不同级别的子目录。此外,六个文件位于目录中。
我们的目标是将rootDir目录的空目录结构复制到另一个位置,以便在目标目录中,我们有:
rootDir
├── dir1
│ └── dir1_1
├── dir2
└── dir3
└── dir3_1
└── dir3_1_1
解决问题可能会想到一个直截了当的思路:首先,我们将rootDir下的所有内容递归复制到目标目录中,然后删除所有文件。
这可以解决问题。但是,如果源目录有很多文件或包含大文件,复制可能会花费很长时间。我们正在寻找更有效的解决方案来解决这个问题。
在本教程中,我们将介绍三种解决此问题的方法:
下面我们详细看看如何复制目录结构。
3. 使用tree命令和 xargs命令
如果我们想克隆一个空的目录结构,我们可以分两步进行:
- 获取我们要创建的所有目录路径
- 将目录路径作为输入,使用mkdir -p创建这些目录
接下来,让我们看看如何使用 tree和 xargs命令执行这两个步骤。
3.1. 获取所有目录路径
正如我们在上一节中看到的,tree命令可以将目录中的内容以树状格式打印出来,非常便于阅读。
此外,我们可以将一些选项组合传递给命令以输出所有目录路径。
首先,让我们进入rootDir目录的父目录并打印其下的所有目录路径:
$ tree -dfi --noreport rootDir
rootDir
rootDir/dir1
rootDir/dir1/dir1_1
rootDir/dir2
rootDir/dir3
rootDir/dir3/dir3_1
rootDir/dir3/dir3_1/dir3_1_1
接下来,让我们浏览一下传递给tree命令的选项并了解它们的含义:
- -d:我们要求 tree命令仅打印目录
- -f:tree命令将为我们打印完整路径
- -i:通常,tree以树格式输出。换句话说,输出中的行缩进很好。但是,此选项将阻止tree打印缩进线
- –noreport:此标志禁止在输出末尾显示摘要报告,例如“ x 目录,y 文件”
3.2. 创建目录
现在,我们有了源目录下所有目录的路径:rootDir。
下一步将很简单:将每个路径传递给mkdir -p命令并在我们的目标目录下创建这些目录。
假设我们要克隆/tmp/test下的 rootDir结构。让我们看看如何完成它:
$ tree -dfi --noreport rootDir | xargs -I{} mkdir -p "/tmp/test/{}"
$ cd /tmp/test
$ tree -a rootDir
rootDir
├── dir1
│ └── dir1_1
├── dir2
└── dir3
└── dir3_1
└── dir3_1_1
6 directories, 0 files
如上面的示例所示,我们将tree命令的输出通过管道传递给xargs命令,以将每个目录路径传递给mkdir命令。
由于我们在源代码rootDir目录的父目录下执行此命令 ,因此我们需要添加我们的目标目录*“/tmp/test”作为xargs*命令读取的每个路径的前缀。
执行命令后,我们再次使用tree命令对其进行验证。但是,这次我们使用*-a选项告诉tree*命令打印所有内容,包括文件。
输出显示我们在*/tmp/test下克隆了干净的rootDir*目录结构,没有任何文件。也就是说,我们已经解决了这个问题。
4. 使用find命令
我们已经学会了使用 tree命令获取源码目录下的所有目录路径。同样,我们也可以使用 find命令获取这些路径:
$ find rootDir -type d
rootDir
rootDir/dir3
rootDir/dir3/dir3_1
rootDir/dir3/dir3_1/dir3_1_1
rootDir/dir1
rootDir/dir1/dir1_1
rootDir/dir2
我们可以将上面的find输出通过管道传递给xargs命令来创建目录,就像我们对 tree输出所做的那样:
$ find rootDir -type d | xargs -I{} mkdir -p "/tmp/test/{}"
$ cd /tmp/test
$ tree -a rootDir
rootDir
├── dir1
│ └── dir1_1
├── dir2
└── dir3
└── dir3_1
└── dir3_1_1
6 directories, 0 files
或者,我们也可以使用 find命令的*-exec *参数来执行 mkdir -p命令:
$ find rootDir -type d -exec mkdir -p "/tmp/test/{}" \;
$ cd /tmp/test
$ tree -a rootDir
rootDir
├── dir1
│ └── dir1_1
├── dir2
└── dir3
└── dir3_1
└── dir3_1_1
6 directories, 0 files
每个找到的目录路径将在mkdir命令中填充占位符“ {} ” 。此外,结尾“ ; ” 是必需的。它是*-exec*选项的命令分隔符。
5. 使用rsync命令
rsync命令是一个强大的命令行文件同步和复制实用程序。
最后,让我们看看如何使用 rsync命令克隆一个干净的目录结构:
$ rsync -av -f"+ */" -f"- *" "/path/to/the/source/rootDir" "/tmp/test"
sending incremental file list
rootDir/
rootDir/dir1/
rootDir/dir1/dir1_1/
rootDir/dir2/
rootDir/dir3/
rootDir/dir3/dir3_1/
rootDir/dir3/dir3_1/dir3_1_1/
sent 258 bytes received 44 bytes 604.00 bytes/sec
total size is 0 speedup is 0.00
$ cd /tmp/test
$ tree -a rootDir
rootDir
├── dir1
│ └── dir1_1
├── dir2
└── dir3
└── dir3_1
└── dir3_1_1
6 directories, 0 files
我们传递给rsync命令的选项看起来有点晦涩。接下来,让我们快速浏览一下。
-a选项要求 rsync进行“存档”复制。换句话说,复制将递归完成并保留文件权限。接下来,-v选项表示冗长。使用此选项,我们可以看到rsync命令所做操作的详细日志。
接下来,棘手的部分-f”+ */”和 -f”- *”来了。
每个*-f选项都定义了一个 过滤器,并且过滤器规则跟在过滤器之后。当rsync*工作时,它将对要复制的文件应用过滤器。
加号“+”表示“包含”,减号“-”表示“排除”。在我们的命令中,我们包括所有目录“+ */”但排除所有文件“- *”。因此,rsync命令只会复制源目录下的目录,并跳过所有文件。