I spent a few hours last night nerding out over an easy way to grab a Mac application’s icon. I sent the basic Bash script and an example Automator action off to the other writers at TUAW. Then there was dinner, a movie, drinks and dessert. I found myself back at it when I got home. I am Jack’s complete inability to leave well enough alone1.

My solution ended up having some tricks in it that I thought were worth sharing, so I’m going to write it up here. This is the first post in a three-part series; an epic tribute to obsessive nerdery. It will probably eventually be summarized into the how-to section, but this is the ever-so-informative longhand version.

Introduction/excusatory verbage

I use application icons quite a bit in my writing for TUAW, so having a quick way to do this is worthwhile. You may never need an application icon as a ready-to-post JPEG, but it’s worthwhile to know tricks such as how to grab, resize and convert images from the command line or how to build an Automator action which combines Finder dialogs, shell scripts and Automator variables.

I’ll start with the basic Terminal commands to lay a foundation.

Finding an app’s icon with the defaults command

On a Mac, every application is actually a folder, referred to as a “bundle”. Within that folder are libraries, executables and resources (like images and icons). There’s also an important file called Info.plist which stores information like the application’s name, identifiers, file types and–most relevant right now–the name of the file containing the application’s icon. The application icon is an an .icns file, stored in AppName.app/Contents/Resources. Its title does not have to be the same as the app’s, so–before we can extract and convert it–we need to get its actual filename from Info.plist.

Info.plist is stored as an XML file (as opposed to a binary plist). As such, we could grep for the filename and run a bunch of Unix commands to parse out the filename, but there’s an easier and more reliable way to do it using the defaults2 command built into OS X. defaults reads plist files and can output the value of specific keys. The key we want is CFBundleIconFile, which holds the icon’s filename. We’ll assume that we’ve already retrieved the name of the app and stored it in a variable called $APP. Here’s what you would run on the command line:

defaults read "/Applications/$APP.app/Contents/Info" CFBundleIconFile

The response may or may not have a file extension (.icns). We want to trim the extension if it exists so that we have a clean foundation to build on. You can do this with several different Unix utilities, including basic bash substitution; we’ll stick with sed3 for now. We pipe (|) the output of the defaults command into sed, and remove “.icns” if it exists:

defaults read "/Applications/$APP.app/Contents/Info" CFBundleIconFile|sed -e 's/\.icns$//'

Converting the icon file to an image format

The .icns format holds multiple versions of the icon, and doesn’t do us much good if we want an image file for the web. Fortunately, the built-in command sips can do some neat tricks with it, including converting it to various formats4. We know what the icon file is titled now, and we know where to look for it. We just build a sips command to read it, convert it and output the result.

The -s format parameter sets an output format in sips. This can be one of many formats, but we’ll use the JPEG format for this example. This will compress the result to a web jpeg with a solid, white background. Using the PNG format instead would preserve the transparency, so if you need to do other image manipulation, you can substitute “png” for “jpeg”.

Let’s use the $APP variable we’ve created to set a default output filename on the Desktop. For now, we’re assuming the app exists in the standard /Applications folder (I’ll show a more complex workflow for locating the app in a bit). We’re also assuming that we’ve stored the result of the above defaults command in $ICON.

OUTFILE="$HOME/Desktop/${APP}_icon.jpg"
sips -s format jpeg "/Applications/$APP.app/Contents/Resources/$ICON.icns" --out $OUTFILE

Now we have the basic pieces we can use to build a script to automate this from the command line. Here’s an example which expects the application name as the only argument, properly cased:

#!/bin/bash

APP=`echo $1|sed -e 's/\.app$//'`
# Removes ".app" if it was passed

ICON=`defaults read /Applications/$APP.app/Contents/Info CFBundleIconFile|sed -e 's/\.icns$//'`
# Removes ".icns" if the Info.plist value includes it
# Harcoded for /Applications, needs to be rewritten for other locations or droplets

## Save icon on Desktop in JPEG format, resized to 256 max width/height
OUTFILE="$HOME/Desktop/${APP}_icon.jpg"
/usr/bin/sips -s format jpeg "/Applications/$APP.app/Contents/Resources/$ICON.icns" --out $OUTFILE

Extra credit: image optimization

Since we’re automating, we can add a final step to the script to handle whatever steps we would normally take next. For example, we could open it in Skitch for quick resizing, or in Acorn for more complex manipulation. If we saved a JPEG to a size we already know is correct for our purposes, we could open it straight in an optimizer such as ImageOptim to quickly improve the compression. We could also make use of a command line utility like ImageMagick, jpegtran or pngcrush, all of which require some installation and extra work. If you have them, great, if not, that’s an article for another time.

For the record, Skitch saves horribly large JPEGs, whereas Acorn saves amazingly well-compressed images. If you resize with Skitch, you’ll want to compress it with something else as well.

Assuming we’re using an application and not a CLI utility, we can use the open command to send the resulting file to the app. We can also avoid some confusion by initially saving the converted icon file to a default temporary directory and then opening it from there. Here’s a revised script that does that, then opens the final image in Acorn for quick resizing and saving to any location. We’ll use the PNG format to preserve the original transparency, and you can substitute any graphics application for Acorn:

#!/bin/bash

APP=`echo $1|sed -e 's/\.app$//'`
# Removes ".app" if it was passed

ICON=`defaults read /Applications/$APP.app/Contents/Info CFBundleIconFile|sed -e 's/\.icns$//'`
# Removes ".icns" if the Info.plist has it
# Harcoded for /Applications, needs to be rewritten for other locations or droplets

## Output file to a temp directory (TMPDIR) as a PNG and open in Acorn for resizing and conversion

OUTFILE="$TMPDIR${APP}_icon.png"
sips -s format png "/Applications/$APP.app/Contents/Resources/$ICON.icns" --out $OUTFILE
open -a "Acorn.app" $OUTFILE

I think you get the idea. You can take this script and easily adjust the output format and destination application. In the next post, we’ll cover making this into a fully customized Bash function, with some extra goodies.

  1. Oh, you got the Fight Club reference, huh? You’re still not a snowflake. 

  2. defaults man page 

  3. sed man page 

  4. “Convert ICNS files with sips” via Macworld.