Contents

递归复制目录,不包括某些子目录

1. 概述

有时,我们希望在复制目录结构时避免复制子目录及其内容。

在本教程中,我们将探索允许我们在 Linux 中复制目录时排除一个或多个子目录的工具。

2. 使用mkdircp进行选择性复制

首先,让我们为我们的实验创建一个目录结构并使用tree 预览内容:

$ mkdir -p /tmp/blogdemo/{dir1,dir2,dir3}/{sub1,sub2,sub3}
$ echo 'text' > /tmp/blogdemo/{dir1,dir3}/{sub1,sub2}/{file1,file2}
$ tree /tmp/blogdemo
/tmp/blogdemo
├── dir1
│   ├── sub1
│   │   ├── file1
│   │   └── file2
│   ├── sub2
│   │   ├── file1
│   │   └── file2
│   └── sub3
├── dir2
│   ├── sub1
│   ├── sub2
│   └── sub3
└── dir3
├── sub1
│   ├── file1
│   └── file2
├── sub2
│   ├── file1
│   └── file2
└── sub3
12 directories, 8 files

对于我们的示例,假设我们想将*/tmp/blogdemo复制到/tmp/blogdemo-new*,但我们不想保留*/tmp/blogdemo/dir1/sub1//tmp/blogdemo/dir3/sub2/*,及其内容。另外,请注意有几个空目录也应该被复制。

在一种天真的方法中,我们可以首先在目标位置使用mkdir创建所需的目录结构 ,然后使用cp复制相关文件:

$ mkdir /tmp/blogdemo-new
$ cd /tmp/blogdemo
$ find -path './dir1/sub1' -prune -o -path './dir3/sub2' -prune -o -type d -printf "%P\0" | xargs -0I{} mkdir '/tmp/blogdemo-new/{}'
$ find -path './dir1/sub1' -prune -o -path './dir3/sub2' -prune -o -type f -printf "%P\0" | xargs -0I{} cp {} '/tmp/blogdemo-new/{}'
$ tree /tmp/blogdemo-new
/tmp/blogdemo-new
├── dir1
│   ├── sub2
│   │   ├── file1
│   │   └── file2
│   └── sub3
├── dir2
│   ├── sub1
│   ├── sub2
│   └── sub3
└── dir3
├── sub1
│   ├── file1
│   └── file2
└── sub3
10 directories, 4 files

第一个命令创建目标目录。然后,我们将当前工作目录更改为*/tmp/blogdemo*,这个位置将是我们在接下来的实验中的当前工作目录。

在第三行中,我们使用*find 命令通过path参数和prune操作排除了不需要的目录。输出包含我们所需目录的空分隔列表。/xargs 命令分隔列表中的每个项目,并使用mkdir*在存储中创建目录。一个空分隔列表允许我们避免由于空格引起的任何问题。

同样在第四行中,我们将find的输出通过管道传输到xargscp以将文件复制到我们的目标位置。然而,正如我们所看到的,这是一个广泛的过程,我们可以使用更好的工具以更简洁的方式完成同样的任务。

3. 使用findcpio进行选择性复制

我们可以使用find和*cpio 在一个命令中结合创建目录和复制文件。如果cpio*命令不可用,我们需要先安装:

$ sudo apt install cpio

让我们回顾一下我们可以使用的标志:

-p, --pass-through
        Pass-through. Read a list of file names from the standard input and copy them to the specified directory.
-0, --null
        Filenames in the list are delimited by null characters instead of newlines
-d, --make-directories
        Create leading directories where needed.

我们可以使用find命令过滤掉不需要的目录,并将输出通过管道传输到cpio进行复制:

$ rm -r /tmp/blogdemo-new
$ find -path './dir1/sub1' -prune -o -path './dir3/sub2' -prune -o -print0 | cpio -pd0 /tmp/blogdemo-new
1 block
$ tree /tmp/blogdemo-new
/tmp/blogdemo-new
├── dir1
│   ├── sub2
│   │   ├── file1
│   │   └── file2
│   └── sub3
├── dir2
│   ├── sub1
│   ├── sub2
│   └── sub3
└── dir3
├── sub1
│   ├── file1
│   └── file2
└── sub3
10 directories, 4 files

在这种方法中,我们使用findcpio在单个命令中过滤并复制了目录结构。

4. 使用tar选择性复制

如果我们使用*tar ,我们可以进一步减少对find*命令的依赖。tar命令允许我们 创建和提取档案。如果我们将创建命令的输出通过管道传输到提取命令,则开销非常小。

首先,让我们回顾一下有趣的标志:

-c, --create
        Create a new archive.
--exclude=PATTERN
        Exclude files matching PATTERN
-f, --file=ARCHIVE
        Use archive file or device ARCHIVE. If ARCHIVE is "-", the output is redirected to stdout
-C, --directory=DIR
        Change to DIR before performing any operations.
-x, --extract, --get
        Extract files from an archive

现在,我们可以创建一个不包括不需要的目录的 tar 存档,并使用tar命令将它们解压缩到新位置:

$ rm -r /tmp/blogdemo-new
$ mkdir /tmp/blogdemo-new
$ tar -cf -  --exclude './dir1/sub1' --exclude './dir3/sub2' . | tar -xC /tmp/blogdemo-new
$ tree /tmp/blogdemo-new
/tmp/blogdemo-new
├── dir1
│   ├── sub2
│   │   ├── file1
│   │   └── file2
│   └── sub3
├── dir2
│   ├── sub1
│   ├── sub2
│   └── sub3
└── dir3
├── sub1
│   ├── file1
│   └── file2
└── sub3
10 directories, 4 files

显然,tar方法更简洁。在第二行中,我们在执行 tar 操作之前确保目标位置存在。cpio方法不需要此步骤。

5. 使用rsync 进行选择性复制

*rsync *命令提供快速增量文件传输。 如果该实用程序不存在,我们可以使用以下命令安装它:

$ sudo apt install rsync

** rsync命令中的exclude标志允许我们指定不想复制的目录:**

$ rm -r /tmp/blogdemo-new
$ rsync -a --exclude={'dir3/sub2','dir1/sub1'} . /tmp/blogdemo-new
$ tree /tmp/blogdemo-new
/tmp/blogdemo-new
├── dir1
│   ├── sub2
│   │   ├── file1
│   │   └── file2
│   └── sub3
├── dir2
│   ├── sub1
│   ├── sub2
│   └── sub3
└── dir3
├── sub1
│   ├── file1
│   └── file2
└── sub3
10 directories, 4 files

我们使用了带有排除标志的大括号扩展。此外,排除标志的参数中不需要前导斜杠。