4

For application specific reasons, I need to copy an entire parent directory into a subdirectory. E.g.,

cp -r ../ tmp/

The problem with this is that it gets stuck in an infinite loop, recursively copying the contents of tmp/ over and over again.

There are a number of ways around this, like tarring the directory and unpacking it in tmp, but I'm wondering if there are any particularly elegant/unixy solutions.

(note: I'm using Apple OS/X).

3

If you start from the parent directory, you can do this with find and GNU cp. Assuming the directory you're in currently (the one containing tmp) is called folder, and that tmp is empty, you'd run

cd ..
find . -path ./folder/tmp -prune -o -type f -exec cp --parents -t folder/tmp {} +

This asks find to list everything under . (which is your old ..), exclude it if it matches ./folder/tmp, and otherwise if it's a file pass it to cp with the --parents option (which tells cp to reconstruct the source hierarchy).

If tmp already has files which also need to be copied, the following variant is slightly less Unix-y (since it uses moreutils' sponge) but avoids skipping the contents of tmp:

cd ..
find . -type f -print0 | sponge | xargs -0 cp --parents -t folder/tmp

You could avoid the use of sponge by saving the list of files to copy in a file somewhere else (but then things get rather less elegant):

cd ..
find . -type f -print0 > /tmp/filelist
xargs -0 cp --parents -t folder/tmp < /tmp/filelist
rm /tmp/filelist

You can avoid the requirement on GNU cp by using cpio instead:

cd ..
find . -type f -print0 > /tmp/filelist
cpio -pmd0 folder/tmp < /tmp/filelist
rm /tmp/filelist

or

cd ..
find . -type f -print0 | sponge | cpio -pmd0 folder/tmp

If tmp is empty you can also avoid the requirement on moreutils:

cd ..
find . -path ./folder/tmp -prune -o -type f -print0 | cpio -pmd0 folder/tmp
7
  • Oh yes, I assumed tmp was empty but that may well be wrong! May 27 '15 at 20:14
  • When I try this I get cp: illegal option -- -. I'm actually 3 levels up in the hierarchy. The command looks like this: find . -path ./docker/app/tmp/ -prune -o -type f -exec cp --parents -t docker/app/tmp/ {} + May 27 '15 at 22:15
  • Does it work if you add -- before {}? As in cp --parents -t docker/app/tmp -- {} +... May 27 '15 at 22:37
  • No -- I get the same error. May 28 '15 at 15:18
  • Hmm... What does cp --version say? Does the last cpio variant work? May 28 '15 at 18:03
0

For interactive use, I have a hack for the analogous case with mv:

cd ..
mv * tmp

mv: cannot move ‘tmp’ to a subdirectory of itself, ‘tmp/tmp’

Ignore the error, everything else got moved :-P. Doesn't work quite the same with cp, it creates an empty tmp/tmp, which is probably not what you want.

0

With pax, to copy . into subdir/tmp, skipping subdir/tmp:

mkdir -p subdir/tmp &&
  pax -rws'|^\./subdir/tmp/.*||' . subdir/tmp

Alternatively, you could do something like:

mkdir -p subdir/tmp/subdir &&
  ln -s tmp subdir/tmp/subdir/tmp &&
  cp -a . subdir/tmp

subdir/tmp/subdir/tmp is then a symlink to itself, to cp won't be able to create anything under it. You can replace it with an empty directory afterwards if you want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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