1. Overview

Sometimes, we want to avoid copying a subdirectory and its contents while copying a directory structure.

In this tutorial, we’ll explore tools that will allow us to exclude one or more subdirectories while copying a directory in Linux.

2. Selective Copy Using mkdir and cp

First, let’s create a directory structure for our experiment and preview the contents with tree:

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

12 directories, 8 files

For our example, let’s say we want to copy /tmp/baeldung to /tmp/baeldung-new, but we don’t want to retain /tmp/baeldung/dir1/sub1/ and /tmp/baeldung/dir3/sub2/, and their contents. Also, note that there are several empty directories which should also be replicated.

In a naive approach, we can create the desired directory structure using mkdir in the target location first, and subsequently copy relevant files using cp:

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

10 directories, 4 files

The first command creates the target directory. Then, we have changed our current working directory to /tmp/baeldung and this location will be our current working directory throughout the rest of our experiment.

In the third line, we have used the find command to exclude the undesired directories using the path parameters and prune action. The output contains a null-delimited list of our desired directories. The xargs command separates each item in the list and creates the directories in the storage using mkdir. A null delimited list allows us to avoid any problem due to whitespace.

Similarly in the fourth line, we have piped the output of find to xargs and cp to copy the files to our target location. However, as we can see, this is an extensive process, and we can employ better tools to accomplish the same in a more succinct way.

3. Selective Copy Using find and cpio

We can combine the creation of directory and copying files in a single command using find and cpio. If the cpio command is not available, we need to install it first:

$ sudo apt install cpio

Let’s review the flags that we can use for our purpose:

-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.

We can filter out undesired directories using the find command and pipe the output to cpio to copy:

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

10 directories, 4 files

In this method, we have filtered and copied the directory structure in a single command using find and cpio.

4. Selective Copy Using tar

We can further reduce dependency on the find command if we use tar. The tar command allows us to create and extract an archive. If we pipe the output of the creation command to the extraction command, there is very little overhead. First, let’s review the interesting flags:

-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

Now, we can create a tar archive excluding undesired directories and extract them in the new location using the tar command:

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

10 directories, 4 files

Clearly, the tar method is more succinct. In the second line, we ensured the target location exists before performing the tar operation. This step was not necessary for the cpio method.

5. Selective Copy Using rsync

The rsync command provides fast incremental file transfer. If the utility does not exist, we can install it using the following command:

$ sudo apt install rsync

The exclude flag in the rsync command allows us to specify directories we do not want to copy:

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

10 directories, 4 files

We have used brace expansion with the exclude flag. Additionally, a leading slash is unwanted in the parameter to the exclude flag.

6. Conclusion

In this article, we have explored four methods to copy a subset of a directory structure to the destination.

In a minimal Linux installation, the tar method is the most suitable. Additionally, tar can also be employed over ssh. However, rsync is the most succinct as it allows us to use specify the directories in a brace expansion.

guest
0 Comments
Inline Feedbacks
View all comments