递归复制目录,不包括某些子目录
1. 概述
有时,我们希望在复制目录结构时避免复制子目录及其内容。
在本教程中,我们将探索允许我们在 Linux 中复制目录时排除一个或多个子目录的工具。
2. 使用mkdir和cp进行选择性复制
首先,让我们为我们的实验创建一个目录结构并使用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的输出通过管道传输到xargs和cp以将文件复制到我们的目标位置。然而,正如我们所看到的,这是一个广泛的过程,我们可以使用更好的工具以更简洁的方式完成同样的任务。
3. 使用find和cpio进行选择性复制
我们可以使用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
在这种方法中,我们使用find和cpio在单个命令中过滤并复制了目录结构。
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
我们使用了带有排除标志的大括号扩展。此外,排除标志的参数中不需要前导斜杠。