Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm trying to write a script to copy files recursively from a particular folder except files A.extn, B/*.extn and C/* where B and C are directories and extn is just some generic extension. This is what I have:

#!/usr/local/bin/zsh
setopt EXTENDED_GLOB
TMPDIR=/tmp/test

cp -pR $(dirname $0)/**~(*.foo/*|*/bar.txt|*.abc|qux.txt) $TMPDIR

However this doesn't do the negation of the pattern as expected. I think I do know why — although the pattern is correct (as seen with echo), cp -R is not aware of the pattern, and enters a directory that it is "not supposed to", and once in there, the pattern is no longer valid.

How do I modify the above to do what I want? I guess it is possible with find and xargs, but I'm drawn towards the clarity of the above and would prefer something similar (but if it's the wrong way to do it, I'd be perfectly happy with a different solution).

share|improve this question
1  
Globbing isn't command-specific, ever. –  vonbrand Apr 23 '13 at 12:22
add comment

2 Answers

up vote 2 down vote accepted

You are correct that the pattern is expanded before cp is run, so is unknown to that command.

You may be able to accomplish what you want by using the --parents option to cp rather than -R. That will only copy the files which match your pattern, but will use the full path name as supplied rather than only the trailing file name.

But, this option isn't portable. AFAIK, it's only supported by the GNU version of cp.

share|improve this answer
    
Thanks, this is what I was looking for. I don't care for portability, as this is for use on this particular machine which has GNU cp. –  Lorem Ipsum Apr 23 '13 at 1:02
add comment

Your pattern may exclude the files you don't want to copy, but it doesn't exclude the directories containing them, and you told cp to copy recursively. So the files are copied anyway. Worse than that, if you have a source tree like

foo
foo/bar.txt
hello
hello/world
hello/world/wibble.txt

then the pattern expands to foo hello hello/world hello/world/wibble.txt so you end up with these files in $TMPDIR:

foo
foo/bar.txt
hello
hello/world
hello/world/wibble.txt
wibble.txt
world
world/wibble.txt

This has nothing to do with the pattern being “no longer valid”. The pattern is expanded by the shell, cp only sees the list of files.

You need to use a copying tool that can exclude files as it's doing the recursive traversal.

You can use zcp for that, but it won't copy the directories, and it won't create target directories automatically. The easiest tool for this job is rsync. Pax is another option.

rsync -a --exclude='*.foo/*' --exclude='bar.txt' --exclude='*.abc' --exclude='/qux.txt' $(dirname $0) $TMPDIR
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.