How to Get a Clean Zip on a Mac

Usually under OS X when you want to zip up a bunch of stuff into a zip file you’ll make use of the Compress command and often times this may be suitable. But, have you ever done this and then unzipped the resulting file on a *nix machine or a Windows machine? If you haven’t and you try it you could be in for a nasty shock bacause as well as putting all the files and folders you did want into the zip file you’ll probably find that Compress also loaded in a whole bunch of Mac specific system files that you really didn’t want. At best they’ll just be annoying, at worst they may cause strange things to happen that take ages to track down.

Consider a specific case that may be familiar to you. You are doing some development work where you have various code repository files, or a backup file, or temporary files, etc., all dispersed throughout a directory tree you want to zip up and transfer. Having to go through and clean these out (assuming you can) is a pain. This is really what spurred me into action – developing for WordPress and creating zip files for installation. I really needed some way to easily make “clean” zip files, by which I mean zip files that contained only the stuff I wanted.

Now I know that if you have been looking on the net for something like this you will likely have found a few examples of how to accomplish this to a greater or lesser extent. These are basically all good but to my mind didn’t go far enough, they lacked flexibility and that’s what I wanted to add.

So, introducing the Clean Directory Compress service, a very simple little script (and I mean simple) created as an OS X Service using the Automator application built in to OS X. The guts of the service are this simple little shell script:

for f in "$@"
do
    # Initialize and empty exclusion file addition
    # and variables for where we are or should be
    excl=""
    path=`dirname $f`
    dir=`basename $f`
    # Move to parent of directory we are going to zip
    cd $path
    # Make sure it is a directory to be zipped
    if [[ -d "$dir" ]]; then
        # Should make sure the exclusion file exists
        # otherwise may get an error
        if [[ -f "./${dir}/__exclude.txt" ]]; then
            excl="-x@./${dir}/__exclude.txt"
        fi
        # Now the zip - explicitly exclude some pattern
        zip -r ${dir}.zip $dir -x \*.DS_Store\* ${excl}
    fi
done

It’s designed to work on folders only (based on the original requirement for use in WordPress development) and takes a list of folders passed in from the Finder based on selected folders in a Finder window. For each folder, get the path and the folder name, change working directory to the containing folder, check that the given folder is actually a directory and then recursively zip the folder into a zip file with the same name as the folder.

So far so good, but what about excluding the things we don’t want? Well that’s what the -x option does for you. The -x option on the zip command is folowed by a list of file patterns and any files or folders whose name match one of those patterns will not be included in the zip. So as you can see there is an instance in the script for excluding the .DS_Store file which is the minimum requirement.

But not only that, and this is where the added flexibility is introduced, the -x@<filename> option allows the specification of a file that contains a further list of file name patterns (one per line) to be excluded. I’ve called this file __exclude.txt and I’ve placed it in the folder that we’re zipping so it’s neat and tidy and each folder can have it’s own exclusion file without any name clashes.

Note however that if you have a folder with a number of sub-folders, etc., and each has it’s own exclusion file that only the exclusion file from the top level folder is used. Of course the exclusion files in the contained folders would be used if you chose to specifically zip one of those folders.

Here’s an example of an exclusion file:

*~
*.git*
*.tmp
*__exclude.txt

Notice the final line? This conveniently specifies that all exclusion files (including this one) should be excluded from the zip file which keeps everything nice and tidy. Of course if you were sending this to another Mac user for further work then you might want to retain the exclusion files at this point.

So if you would like to create a Service with this script simply fire up Automator, select the Utilities library, drag the Run Shell Script action into the active area, select the service to receive selected folders in Finder and pass the input as arguments, paste the above code into the Run Shell Script code area and save this new Service with your chosen name – it will automatically be placed in the correct place on your system and you just then go and enable it through Finder Services Preferences. Here are a couple of screenshots to help out.

From then on if you want a clean zip just highlight a folder, select the service (whatever you choose to call it) and you’ll get a zip file appear.

Of course the script could also be used from the command line with a bit of adaptation for a script header and argument handling.

If you are feeling adventurous you could add your own additional tweaks such as electing for greater compression for smaller file size, exclusion from compression of files that are already using a compressed format, etc.

This was created under Mac OS X 10.6 (Snow Leopard) but there’s no reason to think it wouldn’t work under earlier versions as it only uses the standard built-in command line zip.