Frequently I want to copy files or directories from within one tree and inject them into another tree. In such cases I want to preserve timestamps, ownerships, and permissions, including those of all parent directories on its path, and I don’t want unspecified children of directories nor siblings of files. Let’s look at doing that with the various common tools available: tar, rsync, cp, install, cpio, afio.

We’ll have a list of paths in /tmp/paths and the objective is to copy them from /tmp/source to /tmp/target. Most of these methods face the issue that they create ancestor directories fresh rather than copy them. The solution is to ensure /tmp/paths contains every directory in every path, always listing parents before children. For example, if aa/bb/cc/d.txt is a path, then aa, aa/bb, aa/bb/cc are also paths and are included in the file in that same order.

tar

tar -C /tmp/source -cf - --files-from /tmp/paths --no-recursion \
| \
tar -C /tmp/target -xf - --same-owner --atime-preserve --same-permissions

(If your tar doesn’t have a -C option, you can use the classic workaround as follows.)

(cd /tmp/source && tar -cf - --files-from /tmp/paths --no-recursion) \
| \
(cd /tmp/target && tar tar -xf - --same-owner --atime-preserve --same-permissions)

rsync

rsync -lptgod --files-from=/tmp/paths /tmp/source /tmp/target

This does exactly what we want, but the nice people designing the interface provide an even easier to remember set of options which is equivalent.

rsync -a --no-r --files-from=/tmp/paths /tmp/source /tmp/target

One big advantage of rsync is that you don’t need to include the prefix paths (ancestor dirs) in the paths file. Less prep and an easy-to-remember invocation. Thank you rsync.

cp

while read path; do
  echo /tmp/source/$path
done </tmp/paths \
| \
xargs cp -pd --parents -t /tmp/target

While an interesting invocation of cp it doesn’t meet the requirements; it copies everything to /tmp/target/tmp/source instead. In fact I failed to find a successful invocation of cp, even putting it in a loop (and so losing efficiency), because it declines to copy a directory by itself.

Others

install is very similar to cp, but even less useful for this task (because it doesn’t preserve ownership). cpio has a terrible manpage and afio seems to have fallen out of favour (superseded by cpio). (To be fair, there’s no point them competing with tar and rsync in this market.)

See also: Using rsync to copy just a directory

This used to be useful for splitting files across multiple floppy disks, splitting archives across multiple tape drives, and other cases where individual media wasn’t big enough for what you’d normally bundle as a single file. Recently I had to look at this again to overcome limits on email attachments. I looked around for my old friend bsplit then remembered that tar can do the job (and is ubiquitous).
Unfortunately (but understandably) tar can’t compress and split at the same time, so first step is to compress my 107 MB file

bzip2 -9 ab.log

For splitting the tar archive into suitable chunks, I needed a multiple of 512 guaranteed to be less than 4 MB, so picked 3584:

tar cf ab.tar --multi-volume --tape-length=3584 ab.log.bz2
n ab-p2.tar

You then want to check that the archive is readable:

tar tf ab.tar --multi-volume
n ab-p2.tar

Reconstructing the original file(s) from the archive chunks is just a matter of changing the ‘t’ to an ‘x’:

tar xf ab.tar --multi-volume
n ab-p2.tar