By default, the “cp” command in Linux copies both files and directories. Here’s a basic cp command:
cp -r source_directory destination_directory
The above command will copy all files and directories from “source_directory” to “destination_directory”. The “-r” flag stands for the “recursive” flag takes the directories one by one and copies the contents to the destination recursively. Unfortunately, while it’s possible to exclude either specific directories or even all directories using the cp command, it isn’t straightforward. I’ll show you how to do it, but other commands are easier to execute and even more efficient.
Using “cp” While Omitting Directories
Let’s say we have the following directory structure:
In the above screenshot, I have the “source” directory all ready and I’m going to copy everything to the “destination” directory except for the directories:
The command I use is:
find ./source -maxdepth 1 -type f -exec cp {} ./destination/ \;pe f -exec cp {} ./destination/ \;
And here’s the result:
As you can see, only the three files were carried over and the directories were eliminated. If there were files in those directories, they would not have been copied either.
Here’s a breakdown of how the above command works.
Finding all the Files in the Top Level of the Directory
The command to copy all the files from the source to the destination directory works in two stages. First, we want a way to find all the files that exist in the top-level of the source directory. We do this using the following command:
find ./source -maxdepth 1 -type f
This makes use of the powerful “find” command, and gives it a few options. First, we tell it in which directory to search, restrict it to a depth of “1”, meaning we tell it to ignore all subdirectories, and then restrict the type of object to “file” using the “-f” flag.
Using “exec” to Operate on the Files
The find command includes the ability to execute commands on each of the items that it finds. This is the infamous “exec” command, that all Linux users are told to avoid if possible. In this case, the “exec” flag with its options forms the second part of the “find” command:
-exec cp {} ./destination/ \;
This tells the find command to take each of its results one by one and execute the “cp” command. The double curly brackets – {} – are a placeholder for the item in the list on which it is currently working. As you can see, we use the “cp” command to copy each item to the “destination” folder. The “\;” at the end is an indication that we’ve finished the command, after escaping the semi-colon.
So you see, it’s quite a complicated thing to use “cp” to exclude all folders from the command.
Omitting a Specific Directory Only with “cp”
As we’ve seen, we can omit all directories and folders with “cp”, by simply specifying the “type -f” parameter which excludes all folders. But what if we want to run cp as before, but this time only exclude a specific folder?
For example, if we want to copy everything from ./source to ./destination except for the “folder2” folder, here is the command we will use:
find ./source -mindepth 1 -path ./source/folder2 -prune -o -exec cp --parents -r {} ./destination/ \;
Here, we exclude the “folder2” folder and then proceed with the same command. As you can see, it gets progressively more complicated as you add restrictions. The “cp” command simply wasn’t built to accommodate such requests natively.
Why “cp” Isn’t the Right Tool for Omitting Directories
It seems strange that we need to create such complex commands in cp for something as simple as excluding directories, but it’s true. The reason for this omission is that cp is a tool for a single purpose – to copy stuff from one directory to the other. While the developers could have included more tools to exclude directories, this would have added additional complicates.
Because cp lacks any kind of pattern matching capabilities, we need to integrate it with other tools like “find” that include such matching. The way cp is written, there is no internal logic applied while copying files. The tool takes the set of source files and directories, and then copies them over to the destination in a single context, without any intermediate steps.
Many of Linux’s tools embrace this modular philosophy, where a tool has one job and it does it well. For more complicated jobs, the idea is to combine tools as we did with “find” and “cp”. As a result, the designers decided to omit the functionality to exclude directories.
Cp also maintains the directory structure while copying. If you play around with the code examples I’ve shown, you’ll see that it’s very easy to make a mistake and screw up the directories when you try and exclude them. The recursive manner in which cp operates makes it tricky to exclude files and folders.
Rsync – a Better Option than cp for Omitting Folders
While cp is great for simple copying tasks, the real solution is to use something like rsync when performing more complex exclusion functions. I’d already written an earlier article on how to use “exclude” with rsync, so you can refer to that article for more details.
For excluding folders specifically, a simple command looks like this:
rsync -av --exclude='folder2/' ./source/ ./destination/
Assuming that the “source” and “destination” folders are in the same location and that you’re already in said location, the above command will copy everything from the source to the destination while omitting the “folder2” directory. As you can see, it’s much more straightforward compared to cp.
If you want to exclude all directories, and not just specific ones, you can use the following rsync command instead:
rsync -av --exclude='*/' source_dir/ destination_dir/
The “*/” wildcard pattern matches all directories, so we use it to specify the exclusion of folders. As you can see, it’s more straightforward than “cp”, which requires various flags and integration with other tools like “find”.
Another Option – The “tar” Command for Omitting Folders
Another option if you don’t want to use rsync is the “tar” command. This might seem strange, given that tar is a tool for archival purposes, but it fits perfectly into this use case. Here’s a simple tar command that transfers the contents of one folder into another while omitting all directories:
tar -cf - -C source_dir . --exclude='*/' | tar -xf - -C destination_dir
The above command works in two steps – first, it creates an archive of the contents of “source_dir”, then it pipes it over to another tar command that extracts it to “destination_dir”. You can see that it uses the same exclude flag that rsync uses.
When used as shown above, with piping, tar doesn’t write any intermediate files to the disk, so it can be very fast on the same file network. If you’re transferring over a network, rsync is usually preferred.
Conclusion
We can use the “cp” command to omit directories, but the process is painful since we have to bring in external tools like “find” to perform the pattern matching and exclusions. It’s doable but messy. Instead, consider using tools like rsync and tar that have exclusions and pattern matching built into them, and also bring performance benefits that cp doesn’t have.
I’m a NameHero team member, and an expert on WordPress and web hosting. I’ve been in this industry since 2008. I’ve also developed apps on Android and have written extensive tutorials on managing Linux servers. You can contact me on my website WP-Tweaks.com!
Leave a Reply