How to Take Mac Files Out of Quarantine

Chances are you’ve experienced the behaviour under OS X where you go to start up an application or access a file and a pop-up appears warning you that the application/file has been downloaded from the Internet and are you sure you trust it? Well, this application or file is in “quarantine” until you explicitly give the nod that you think it’s safe because (hopefully) you downloaded it from a reputable source and you actually know what it is.

Now, assuming that the user you are logged into your Mac as has the correct privileges the quarantine attribute will be removed and you’ll never see that message again for that application or file. For a lot of people this tends to be the case because of the way they operate their machine – perhaps only one login or if there are separate logins they don’t share downloaded applications or files.

However, if you have a more controlled operational environment – perhaps having multiple logins for different “personas” such as work, personal, etc., and/or have a specific login for administration activities such as application download and install – then it’s not always so simple. Additionally, if you are a developer and you tend to download packages such as WordPress themes or plugins (or core WordPress itself) and then want to edit more than a few files in that download, you’ll soon tire of being asked the same question every time you try to access a new file.

So, following on from my handy Clean Directory Compress service, here comes the Remove From Quarantine service that can be applied to any directory and operates recursively down through all sub-directories to remove the quarantine attribute from every file.

First, let’s take a look at how a file in quarantine look when you do a directory listing in Terminal – something like this:

ambiguous$ ls -dl wp-admin
drwxr-xr-x@ 92 ambiguous  staff  3128 15 Feb 11:04 wp-admin

See the @ sign after the directory permissions, that means there are some extended attribute(s) present – so to see what they are:

ambiguous$ xattr wp-admin
com.apple.quarantine

and there you have it, the quarantine attribute. Now in general there may be other extended attributes present on a file or directory but we can deal with each individually leaving the others unchanged.

There is no man entry for the xattr command but we can see what we can do with it by specifying the -h option:

ambiguous$ xattr -h
usage: xattr [-l] [-r] [-v] [-x] file [file ...]
 xattr -p [-l] [-r] [-v] [-x] attr_name file [file ...]
 xattr -w [-r] [-v] [-x] attr_name attr_value file [file ...]
 xattr -d [-r] [-v] attr_name file [file ...]

The first form lists the names of all xattrs on the given file(s).
The second form (-p) prints the value of the xattr attr_name.
The third form (-w) sets the value of the xattr attr_name to the string attr_value.
The fourth form (-d) deletes the xattr attr_name.

options:
 -h: print this help
 -r: act recursively
 -l: print long format (attr_name: attr_value and hex output has offsets and
 ascii representation)
 -v: also print filename (automatic with -r and with multiple files)
 -x: attr_value is represented as a hex string for input and output

From this help information you can see that the form of the command of interest is the fourth form which enables an attribute to be deleted. So let’s see that in action:

ambiguous$ xattr -d com.apple.quarantine wp-admin
ambiguous$ ls -dl wp-admin
drwxr-xr-x  92 ambiguous  staff  3128 15 Feb 11:04 wp-admin
ambiguous$ xattr wp-admin
ambiguous$

The command on line 1 deletes the attribute from the wp-admin directory; the directory listing on lines 2 & 3 shows that there are now no extended attributes attached to the directory; and finally on lines 4 & 5 applying the xattr command to the directory again shows that there are now no extended attributes. This means we could now work with this directory without being asked whether it should be considered safe or not.

So you can apply the xattr command to individual files and directories but the most benefit comes from applying it recursively starting at a top-level directory. Have a look at this (abbreviated) directory listing of the wp-admin directory content:

ambiguous$ ls -l wp-admin
total 1888
-rw-r--r--@  1 ambiguous  staff  47460 25 Jul  2010 admin-ajax.php
-rw-r--r--@  1 ambiguous  staff   1341 25 Apr  2010 admin-footer.php
-rw-r--r--@  1 ambiguous  staff    403 11 Aug  2008 admin-functions.php
-rw-r--r--@  1 ambiguous  staff   5357 27 May  2010 admin-header.php
-rw-r--r--@  1 ambiguous  staff    592  5 Mar  2009 admin-post.php
-rw-r--r--@  1 ambiguous  staff   6086 12 Jul  2010 admin.php
-rw-r--r--@  1 ambiguous  staff   2281 23 May  2010 async-upload.php
-rw-r--r--@  1 ambiguous  staff   8831  3 Jun  2010 comment.php
drwxr-xr-x@ 64 ambiguous  staff   2176 29 Dec 21:15 css
-rw-r--r--@  1 ambiguous  staff  12816 11 Jun  2010 custom-background.php

where you can see the extended attribute indicator against the files and directory. By executing the xattr delete recursively with the -r option we can remove the attribute from all the files and sub-directories in a recursive manner.

ambiguous$ xattr -d -r com.apple.quarantine wp-admin
ambiguous$ ls -l wp-admin
total 1888
-rw-r--r--   1 ambiguous  staff  47460 25 Jul  2010 admin-ajax.php
-rw-r--r--   1 ambiguous  staff   1341 25 Apr  2010 admin-footer.php
-rw-r--r--   1 ambiguous  staff    403 11 Aug  2008 admin-functions.php
-rw-r--r--   1 ambiguous  staff   5357 27 May  2010 admin-header.php
-rw-r--r--   1 ambiguous  staff    592  5 Mar  2009 admin-post.php
-rw-r--r--   1 ambiguous  staff   6086 12 Jul  2010 admin.php
-rw-r--r--   1 ambiguous  staff   2281 23 May  2010 async-upload.php
-rw-r--r--   1 ambiguous  staff   8831  3 Jun  2010 comment.php
drwxr-xr-x  64 ambiguous  staff   2176 29 Dec 21:15 css
-rw-r--r--   1 ambiguous  staff  12816 11 Jun  2010 custom-background.php

As you can now see the extended attribute indicator has gone and the files and directories are now out of quarantine.

You might be quite happy to just apply this command in a Terminal window, but for added convenience it can be made into a service. So the final piece of the puzzle is to insert the following code block into an Automator service definition (see the Clean Directory Compress service post for the process to follow and/or read the Automator Help and Documentation) and you can easily recursively remove a directory and it’s content from quarantine in the Finder.

for f in "$@"
do
    path=`dirname $f`
    dir=`basename $f`
    cd $path
    if [[ -d "$dir" ]]; then
        xattr -d -r com.apple.quarantine ${dir}
    fi
done

Hope that helps to make your development process just a little bit easier.

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.