Grabbing a Mac app’s icon: advanced Bash usage
In the previous post in this series, we looked at the basic Terminal commands we’d use to grab a Mac application’s icon from the command line. In this post, we’ll flesh out the script a little and turn it into a Bash function with some added features:
- Logic to locate the app if it exists outside of /Applications
- A check using
sips
to find the maximum available image size - Allow user input to set the final output size
- Allow the user to decide whether to open the result in Preview.app
We’ll also look at a very, very cool trick for adding tab-completion for application names to the open -a
command, as well as our geticon()
function. Yes, it’s that nerdy.
In the next post, we’ll use Automator to make this into a droplet we can drag apps onto from Finder. If you don’t give a flying fire truck about Terminal, you can skip straight there and create something useful without touching the command line. This one’s for the nerds (and wannabe nerds).
A for loop directory search
We’ll use a bash for
loop to search some predefined locations. You could also use mdfind
1 to let Spotlight locate the app, but we’ll keep it simple for now. I’ve set it up to look in some standard locations for the file:
APPDIR=''
for dir in "/Applications/" "/Applications/Utilities/" "/Developer/Applications/" "/Developer/Applications/Utilties/"; do
if [[ -d ${dir}$APP.app ]]; then
APPDIR=$dir
break
fi
done
$APPDIR is now set to the location that $APP was found, or to ‘’ (blank) if it wasn’t found in any of the specified folders. We can check for that in the next part and fail gracefully if we didn’t find the specified app.
Getting the maximum image size
Modern Mac icons generally have a pixel width of 512 or greater, but some legacy applications’ icons are 256px or smaller. Finding the maximum pixel width of the image allows us to avoid creating a distorted image as a result of sizing up beyond the largest image in the .icns file. The sips
command can do this with a little help from awk
2 to clean up the output. The following command gets the pixelWidth
property from the icon file, grabs the last line of output and removes extraneous text to leave us with just a number:
MAXAVAIL=`sips -g pixelWidth "${APPDIR}$APP.app/Contents/Resources/$ICON.icns"|tail -1|awk '{print $2}'`
We could use the number we found to output a file with the maximum dimensions, but since we’re making a general-purpose function, we’ll ask the user what they want.
Getting user input
There are a few ways to offer options in Bash. This is the simplest route I know. It doesn’t innately allow for a lot of error checking; the user could enter letters instead of numbers or an unattainable dimension, for example. We’ll add some basic checks, but we’ll assume that you can properly enter a number when asked. You’re smart like that.
echo -n "Enter max pixel width ($MAXAVAIL): "
read MAX
if [[ $MAX == '' || $MAX -gt $MAXAVAIL ]]; then
MAX=$MAXAVAIL
fi
We echo the prompt (the -n keeps it from echoing a newline), and we use our previously determined $MAXAVAIL variable to set a cap. If the user’s answer is empty or larger than $MAXAVAIL, we default to $MAXAVAIL.
The whole shebang
Here’s a complete Bash function that you can paste at the end of your ~/.bash_profile
. It offers to open the resulting image in Preview.app, but you can easily modify that to whatever app makes sense in your workflow.
function geticon() {
APP=`echo $1|sed -e 's/\.app$//'`
APPDIR=''
for dir in "/Applications/" "/Applications/Utilities/" "/Developer/Applications/" "/Developer/Applications/Utilties/"; do
if [[ -d ${dir}$APP.app ]]; then
APPDIR=$dir
break
fi
done
if [[ $APPDIR == '' ]]; then
echo "App not found"
else
ICON=`defaults read "${APPDIR}$APP.app/Contents/Info" CFBundleIconFile|sed -e 's/\.icns$//'`
OUTFILE="$HOME/Desktop/${APP}_icon.jpg"
MAXAVAIL=`sips -g pixelWidth "${APPDIR}$APP.app/Contents/Resources/$ICON.icns"|tail -1|awk '{print $2}'`
echo -n "Enter max pixel width ($MAXAVAIL): "
read MAX
if [[ $MAX == '' || $MAX -gt $MAXAVAIL ]]; then
MAX=$MAXAVAIL
fi
/usr/bin/sips -s format jpeg --resampleHeightWidthMax $MAX "${APPDIR}$APP.app/Contents/Resources/$ICON.icns" --out "$OUTFILE" > /dev/null 2>&1
echo "Wrote JPEG to $OUTFILE."
echo -n 'Open in Preview? (y/N): '
read ANSWER
if [[ $ANSWER == 'y' ]]; then
open -a "Preview.app" "$OUTFILE"
fi
fi
}
Extra credit: tab completion for app names
Before we get into making this into a GUI using Automator, wouldn’t it be cool if you could autocomplete an app name from the command line when you use this? This is where you find out that I can nerd anything to death, but let’s do it.
First, we need some functions to build a list of all of our available applications and provide them to Bash’s completion command. I’m modifying a script from Kim Holburn for this, updated to work on 10.6 and provide case-insensitive completion. Just put the file in your user’s home directory for now. When we’re done, it will also provide application name tab completion for open -a
(and o
if you alias o=”open -a” in your .bash_profile). Click the link below to open the script, then save it to your home folder as “.app_completions”.
Once you have that file located in ~/.app_completions
, we just need to reference it in .bash_profile
and add a few settings. Put this near the top of ~/.bash_profile
:
source ~/.app_completions
bind "set completion-ignore-case on"
set show-all-if-ambiguous on
alias o="open -a"
At the command line, run source ~/.bash_profile
. Now, assuming you’ve also added the previous geticon
function to .bash_profile
, you should be able to type geticon term
and hit tab right after the ‘m’ to have it complete to “Terminal.app” automatically. This makes it a lot easier to get the app’s name, spacing and capitalization exactly right. You’ve also aliased “o” to launch an app in the process. Type o auto
to complete to “Automator.app” and launch it.
We’ll dig into Automator next and turn this whole thing into something pretty.