Mac OS X Commandline: ditto

One of the things I love about Mac OS X is that, beneath the pretty and functional surface most people see, there’s a complete UNIX environment with all the tools a commandline jockey needs. And if something is missing, just install Xcode and homebrew and you’re good.

With this post I will kick off a series of short articles, published at highly irregular intervals, about the commandline utilities that come with Mac OS X and isn’t part of other UNIX-like operating systems.

First up: ditto.

Ditto can “… copy directory hierarchies, create and extract archives”, and it does so with a complete understanding of the peculiarities of Mac OS X filesystem resource forks, extended attributes, Access Control Lists and quarantine information.

All of these features are on by default since Mac OS X 10.5, as this is almost always what you want for backup purposes. There are quite self-explanatory --norsrc, --noextattr, --noacl and --noqtn options to shut those features off.

Copying a directory is straightforward:

% ls src
a  b  c
% ditto src dest
% ls -F
dest/ src/
% ls dest
a  b  c

Use the -v or -V options to make ditto more verbose about what it’s doing.

When creating an archive using the -c option, files are per default stored as CPIO archives, or as ZIP archives with the -k option. CPIO archives can optionally be compressed with either gzip or bzip2, the -z and -j options respectively.

A feature of ditto is that it does not embed the parent directory in the archive by default, use --keepParent for this. You also need to explicitly specify the output directory when extracting an archive. There’s a certain logic to this, given the directory copying capabilities of ditto, but it’s counterintuitive compared to other common commandline archiving utilities. Let’s see an example, first without --keepParent:

% ls dir/
a  b  c
% ditto -ck dir     # Create archive.
% ls -F
% ditto -xk output  # Extract archive to ‘output’.
% ls output
a  b  c

This is not how tar works for example. To get a more tar-like behaviour, use --keepParent when creating the archive:

% ditto --keepParent -ck dir
% ditto -xk output
% ls -F output
% ls -F output/dir
a  b  c

See? Nevermind that tar defaults to the current directory as output directory and needs a -C option to output to another directory.

If you are creating an archive to share with someone not on a Mac be sure to use the --norsrc option to avoid creating ._* files on the target system. There’s no file exclusion option for ditto, so it is not directly possible to avoid archiving the .DS_Store files. A workaround is the --bom option, but then you need to create a BOM file, and that’s overkill in most situations, and—I think—another post entirely.