75

Is there a way to use the cp command to copy a directory and exclude certain files/sub-directories within it?

5 Answers 5

103

Use rsync:

rsync -av --exclude='path1/to/exclude' --exclude='path2/to/exclude' source destination

Note that using source and source/ are different. A trailing slash means to copy the contents of the folder source into destination. Without the trailing slash, it means copy the folder source into destination.

Alternatively, if you have lots of directories (or files) to exclude, you can use --exclude-from=FILE, where FILE is the name of a file containing files or directories to exclude.

-av means archive mode and verbose.

--exclude may also contain wildcards, such as --exclude=*/.svn*.

Copied From: https://stackoverflow.com/a/2194500/749232

If you want to use cp itself:

find . -type f -not -iname '*/not-from-here/*' -exec cp '{}' '/dest/{}' ';'

This assumes the target directory structure is the same as the source's.

Copied From: https://stackoverflow.com/a/4586025/749232

1
  • 2
    The word "contents" in "copy the contents of the folder..." is a major keyword to understand the sentence. It should be bolded and highlighted.
    – M J
    Mar 1, 2021 at 1:33
13

Late into the game but here is a very different solution using plain Bash and cp: you can use a global file specification while having some files ignored.

Assume the directory contains the files:

$ ls *
listed1  listed2  listed3  listed4  unlisted1  unlisted2  unlisted3

Using the GLOBIGNORE variable:

$ export GLOBIGNORE='unlisted*'
$ ls *
listed1  listed2  listed3  listed4

Or with more specific exclusions:

$ export GLOBIGNORE='unlisted1:unlisted2'
$ ls *
listed1  listed2  listed3  listed4  unlisted3

Or using negative matches:

$ ls !(unlisted*)
listed1  listed2  listed3  listed4

This also supports several unmatched patterns:

$ ls !(unlisted1|unlisted2)
listed1  listed2  listed3  listed4  unlisted3
2
  • 3
    For negative matches you need to have extended globs enabled: shopt -s extglob, also you don't need to export GLOBIGNORE: it's supposed to modify the behaviour of the current shell, most child programs wouldn't care about it.
    – muru
    Jun 13, 2016 at 22:14
  • BTW, to turn off extended glob, run shopt -u extglob.
    – Rockallite
    Dec 10, 2019 at 0:44
4

Quick Start

Run:

rsync -av --exclude='path1/in/source' --exclude='path2/in/source' [source]/ [destination]

Notes

  • -avr will create a new directory named [destination].
  • source and source/ create different results:
    • source/ — copy the contents of source into destination.
    • source — copy the folder source into destination.
  • To exclude many files:
    • --exclude-from=FILEFILE is the name of a file containing other files or directories to exclude.
  • --exclude may also contain wildcards:
    • e.g. --exclude=*/.svn*

Modified from: https://stackoverflow.com/a/2194500/749232


Example

Starting folder structure:

.
├── destination
└── source
    ├── fileToCopy.rtf
    └── fileToExclude.rtf

Run:

rsync -av --exclude='fileToExclude.rtf' source/ destination

Ending folder structure:

.
├── destination
│   └── fileToCopy.rtf
└── source
    ├── fileToCopy.rtf
    └── fileToExclude.rtf
1
  • I generally use -avh --progress as a rule, add --ignore-existing if copying to a destination that gets regular backups and want to skip files that match created/modified time and size, and add --delete-after if deleted files in source and destination should reflect those deletions. -z is compression and can help make the transfer a little faster if backing up to a remote location (fairly unnecessary on LAN). --bwlimit=10000 (rate given in KB/s) will limit speed to 10MB/s and is helpful for long backup if concerned about hampering total bandwidth. Aug 21, 2020 at 5:54
2

You can use cp with the ! character.

For example, to exclude the file or files file.txt, test.jpg and the directory nodir, while copying all others from the source directory to the destination directory, you may run:

cp source/!(file.txt|test.jpg|nodir) destination

If you have, for example, a file structure as this one:

.
├── destination
└── source
    ├── file.rtf
    ├── file.txt
    ├── test.jpg
    ├── yes
    |    └── test.jpg 
    └── nodir
         └── other.jpg

The result after running the above command is:

.
└── source (not modified)
└── destination
    ├── file.rtf
    └── yes
         └── test.jpg 

Here is an article with information on rsync and cp with exclude: How to cp copy and exclude internal files or directories (equivalent to rsync –exclude)

1
  • if you use this script in bash file, add #!/bin/bash shopt -s extglob for remove error on (
    – Cadot.eu
    Mar 22, 2022 at 12:15
0

Everyone seems to answering with "Use rsync", while I agree that is a very powerful tool its also overkill and presents some risks. The OP asked how to do it with cp and I found this worked combining some of the recommendations others made.

cp -rf !(backup) backup/

This was a simple experiment in copying all the files in a directory to a file called backup in the same directory without cp worrying about skipping a recursive call. Hope this helps.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .