How to use 'cp' command to exclude a specific directory?

551,754

Solution 1

rsync is fast and easy:

rsync -av --progress sourcefolder /destinationfolder --exclude thefoldertoexclude

You can use --exclude multiples times.

rsync -av --progress sourcefolder /destinationfolder --exclude thefoldertoexclude --exclude anotherfoldertoexclude

Note that the dir thefoldertoexclude after --exclude option is relative to the sourcefolder, i.e., sourcefolder/thefoldertoexclude.

Also you can add -n for dry run to see what will be copied before performing real operation, and if everything is ok, remove -n from command line.

Solution 2

Well, if exclusion of certain filename patterns had to be performed by every unix-ish file utility (like cp, mv, rm, tar, rsync, scp, ...), an immense duplication of effort would occur. Instead, such things can be done as part of globbing, i.e. by your shell.

bash

man 1 bash, / extglob.

Example:

$ shopt -s extglob
$ echo images/*
images/004.bmp images/033.jpg images/1276338351183.jpg images/2252.png
$ echo images/!(*.jpg)
images/004.bmp images/2252.png

So you just put a pattern inside !(), and it negates the match. The pattern can be arbitrarily complex, starting from enumeration of individual paths (as Vanwaril shows in another answer): !(filename1|path2|etc3), to regex-like things with stars and character classes. Refer to the manpage for details.

zsh

man 1 zshexpn, / filename generation.

You can do setopt KSH_GLOB and use bash-like patterns. Or,

% setopt EXTENDED_GLOB
% echo images/*
images/004.bmp images/033.jpg images/1276338351183.jpg images/2252.png
% echo images/*~*.jpg
images/004.bmp images/2252.png

So x~y matches pattern x, but excludes pattern y. Once again, for full details refer to manpage.


fishnew!

The fish shell has a much prettier answer to this:

🐟 cp (string match -v '*.excluded.names' -- srcdir/*) destdir

Bonus pro-tip

Type cp *, hit CtrlX* and just see what happens. it's not harmful I promise

Solution 3

Why use rsync when you can do:

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

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

Solution 4

cp -r `ls -A | grep -v "c"` $HOME/

Solution 5

The easiest way I found, where you can copy all the files excluding files and folders just by adding their names in the parentheses:

shopt -s extglob
cp -r !(Filename1 | FoldernameX | Filename2) Dest/
Share:
551,754
David Liu
Author by

David Liu

Updated on February 01, 2022

Comments

  • David Liu
    David Liu over 2 years

    I want to copy all files in a directory except some files in a specific sub-directory. I have noticed that cp command didn't have the --exclude option. So, how can I achieve this?

  • Paulo Scardine
    Paulo Scardine over 13 years
    watch out for directory entries containing spaces.
  • Shawn Chin
    Shawn Chin over 13 years
    You can skip the extra ls and simply do cp !(file1|file1) dest.
  • James Murty
    James Murty about 12 years
    I think you need the -path argument to test path hierarchies, not -iname
  • Matthew Wilcoxson
    Matthew Wilcoxson over 11 years
    And you'll also need a semi-colon at the end: find . -type f -not -path '*/not-from-here/*' -exec cp '{}' '/dest/{}' \;
  • Linus Kleen
    Linus Kleen over 11 years
    @MatthewWilcoxson Thanks Matthew, go ahead an edit my answer.
  • Matthew Wilcoxson
    Matthew Wilcoxson over 11 years
    Agreed, you can't beat the simplicity and power of --exclude
  • Matthew Wilcoxson
    Matthew Wilcoxson over 11 years
    Wow, it won't let me: "Edits must be at least 6 characters" !
  • Linus Kleen
    Linus Kleen over 11 years
    @MatthewWilcoxson Meh. Those restrictions will be lifted, as soon as you gain a little more rep. I edited the answer accordingly. Thanks again!
  • Beebee
    Beebee almost 11 years
    is the thefoldertoexclude relative to the sourcefolder or the current working dir? thanks
  • orkoden
    orkoden over 10 years
    It's relative to the source folder. This will exclude the folder source/.git from being copied. rsync -r --exclude '.git' source target
  • Xavi Montero
    Xavi Montero over 10 years
    Maybe I'm wrong but I think it's a good practice to add "switches" before "parameters". Also the man page of rsync reports --exclude usable as with the "=" syntax or without. So to standarize across operating systems, I'd use rsync -av --progress --exclude="thefoldertoexclude" sourcefolder /destinationfolder - anyway upvote for the rsync instead of the find, as you can easily use absolute paths for the source while in the find it's trickier as it uses the {} in the dst.
  • Linus Kleen
    Linus Kleen over 10 years
    Why, thank you, @Henning for pointing that out. Although this answer is now more than three years old, it never occurred to me that rsync might actually be a shorter command. Wait. No. It did. That's why I mentioned it. Now it's up to you to supply that answer right here. Just because mine is the accepted answer doesn't mean yours could outscore it. Go for it.
  • geneorama
    geneorama about 10 years
    Doesn't work for me. I get -bash: !: event not found
  • AmokHuginnsson
    AmokHuginnsson almost 10 years
    I am sorry but rsync is not installed by default on any system I know of, tar OTOH is.
  • imkost
    imkost over 9 years
    shopt -s extglob (execute this to enable ! in cp, rm and others)
  • ulidtko
    ulidtko almost 9 years
    @MikhailGolubtsov perhaps that's because globbing is not recursive and works one level at a time. Edited out. P.S: it works in zsh though.
  • Osman Mamun
    Osman Mamun almost 9 years
    Can I use it in case of file transfer across machines (like scp)?
  • thebugfinder
    thebugfinder almost 9 years
    @XaviMontero definitely not, as you add many of those switches, it will get lost what is the actual folder being copied. imaging having, 5 exclude switches. Some args are good to have them at the end, i think
  • tomekwi
    tomekwi almost 9 years
    Not pretty maybe – but this is the only option I’ve found here which works with cp and a standard POSIX shell like sh.
  • Masker
    Masker over 8 years
    I think slash at wrong place,rsync -av --progress sourcefolder/ destinationfolder --exclude thefoldertoexclude
  • sobi3ch
    sobi3ch over 8 years
    @mamun Of course e.g. from remote server to local machine: $ rsync [email protected]:/var/www/mypage /var/www/mylocalpage/ or from local to remote $ rsync /var/www/mylocalpage/ [email protected]:/var/www/mypage
  • sobi3ch
    sobi3ch over 8 years
    Unfortunately I found that rsync are ~4x slower then cp command :( at least on my VM even when I'm including big .git directory in cp task and excluding in rsync.
  • LAL
    LAL over 8 years
    Do not use -laR. it add string that interfere with cp. cp $(ls folder/!exclude_folder0|exclude_folder1)) dest
  • Paulo Neves
    Paulo Neves over 7 years
    Unfortunately rsync cannot coppy as symlink :(
  • tibbus
    tibbus about 7 years
    This command is purely magic, it doesn't overwrite the unchanged files, it also shows you the time and the transfer speed, also shows you which file where copied only the different ones !!
  • atwellpub
    atwellpub almost 7 years
    Worked for me in Windows 10 .sh
  • Kennet Celeste
    Kennet Celeste over 6 years
    how can I exclude multiple folders/files with this? thanks
  • Oleg Neumyvakin
    Oleg Neumyvakin about 6 years
    @Yugi you can add --exclude multiple times for another folders
  • taffit
    taffit about 6 years
    Nice pro-tip! This way you can remove single items easily. Thanks a lot!
  • Reishin
    Reishin almost 6 years
    @Henning why not rsync? Coz it may be not present in the system! while find, cp is always on their places. Or you from kind of guys, who installed 2gigs of stuff to do simple things?
  • xuhdev
    xuhdev over 5 years
    What if /dest/ do not have required directory structure?
  • Linus Kleen
    Linus Kleen over 5 years
    @xuhdev Hence the disclaimer in the answer. It only works when the target directory structure is the same as the source's.
  • ElectRocnic
    ElectRocnic about 5 years
    Made a shell function which simplifies the usage for custom source path and exclusion of just one file or directory: # $1 = source path # $2 = destination path # $3 = filter copy_from_source_to_destination_except_filter() { cp -r $(ls -A $1 | grep -v -w $3 | awk -v path=$1 '{printf "%s/%s ", path, $1}') $2 }
  • Sérgio
    Sérgio about 5 years
    a simple solution that take cares of special characters and white spaces
  • Sérgio
    Sérgio about 5 years
    fails with directories with spaces
  • Rockallite
    Rockallite over 4 years
    BTW, to turn off extended pattern matching features in Bash, run setopt -u extglob.
  • reducing activity
    reducing activity over 4 years
    What is the difference/improvement compared to the top answer?
  • reducing activity
    reducing activity over 4 years
    What is the difference/improvement compared to the top answer?
  • reducing activity
    reducing activity over 4 years
    Thanks for explaining parameters, unlike the current top answer!
  • Robert Talada
    Robert Talada about 4 years
    This answer is dramatically underrated. This is the most compatible, easiest to read and easy to understand answer. Kudos, I don't know why I didn't think of it.
  • kungfooman
    kungfooman about 4 years
    Thanks @RobertTalada, how far will the answer go from now? ‎️‍🌈
  • ayorgo
    ayorgo over 3 years
    "... an immense duplication of effort..." shouldn't it be just a one-liner: exclude paths from the list that match a regex? How the file manipulating utilities like cp don't support this most simple and straightforward use case out of the box is beyond me. Thanks for the tip though!
  • ulidtko
    ulidtko over 3 years
    @ayorgo well yes it "should" — but in C, a oneliner can't do much: multiply some ints and maybe move a pointer, that's it. Even ignoring the source level, regex matching in C involves additional library dependency and additional machine code output — now multiply this by the number of commands, and you've got nontrivial (unbounded?..) overhead. At least that's my understanding why it was "refactored" to the shell; I can totally relate to the subpar UI aspect of it, but hopefully you can also see the technical justification now. Best wishes!
  • mbomb007
    mbomb007 over 3 years
    @geneorama This happens if history substitution is enabled. serverfault.com/a/208414/352016
  • Arch Stanton
    Arch Stanton over 3 years
    @Sérgio I haven't tested it but cp -r "$(ls -A | grep -v "c")" $HOME/ should work. The command in the answer fails because there cp operates on the output of ls -A | grep -v "c", which is unquoted and therefore breaks on spaces. "$(…)" is the same as "`…`" but easier on the eyes.
  • qräbnö
    qräbnö over 3 years
    My fix for that - somewhere below: stackoverflow.com/a/65485164/1707015
  • DarkTrick
    DarkTrick over 3 years
    @orkoden Here on Ubuntu exclude works relative to the current working dir (which is really unintuitive)
  • Hódos Gábor
    Hódos Gábor over 3 years
    @XaviMontero and if you want to exclude multiple folders you can use rsync like this rsync -av --progress --exclude={"thefoldertoexclude","anotherfolder","folder2"} sourcefolder /destinationfolder
  • Xavi Montero
    Xavi Montero over 3 years
    Ahh good to know @HódosGábor. I always gave multiple --exclude options by typing them all. Nice compact format!
  • ddzzbbwwmm
    ddzzbbwwmm about 3 years
    how about multiple folder to exclude?
  • Abdull
    Abdull about 3 years
    Gave it a try. Current version of your answer creates in the first step directories. Because this step currently contains -not -path '*/not-from-here/*', it will create directory ./not-from-here. Probably, this is not intended. Therefore, for the first step (directory creation), you probably want -not -path '*/log' instead.
  • Kasun Siyambalapitiya
    Kasun Siyambalapitiya about 3 years
    @hank what is the use of -a in above as per the man page it is for archiving or for appending data onto shorter files?
  • PJ Brunet
    PJ Brunet about 3 years
    The nice thing about tar, you can use exclude.tag files to ignore directories stackoverflow.com/a/13280610/722796 also gnu.org/software/tar/manual/html_node/exclude.html
  • Robino
    Robino almost 3 years
    Obvious drawback is that you might be avoiding copying something because it's too big.
  • gekkedev
    gekkedev almost 3 years
    @reducingactivity less obsolete flags
  • Fahad Munir
    Fahad Munir almost 3 years
    --exclude /node_modules/ will exclude node_modules anywhere in the source tree. So powerful while copying a bunch of JavaScript/Node.js project folders.
  • Hawkeye Parker
    Hawkeye Parker almost 3 years
    I feel like the '-a' in the first answer is better than plain old -r: explainshell.com/explain?cmd=rsync+-a
  • Dalbergia
    Dalbergia almost 3 years
    @ddzzbbwwmm You probably figured it out by now, but for posterity's sake: you can add multiple --exclude flags, like: --exclude 'foo' --exclude 'bar'
  • Goran B.
    Goran B. almost 3 years
    @reducingactivity nothing much but simple to digest as the expression is shorter, just my personal preference
  • t7e
    t7e about 2 years
    What if I want to exclude folders as well?
  • t7e
    t7e about 2 years
    Nice tip, but it does not work in sh.
  • t7e
    t7e about 2 years
    what about POSIX sh?
  • ulidtko
    ulidtko about 2 years
    @t7e in that case, I'm afraid, you're restricted to find ... | xargs.