Welcome
This is the homepage of Marc Liyanage.
You probably came here for the software in the Mac OS X Software
category...
I just published a free new Mac OS X application. Say Hello to Album Artwork Assistant :-)
Album Artwork Assistant finds album cover artwork on the Internet and adds it to music track files in iTunes. Other tools like it exist, but none of them worked the way I wanted, or they cost too much.
Feel free to try it out if you need something like this, and let me know if you find any bugs.
I wrote the AppleScript below to eject one or more selected volumes in the Finder. If a volume represents a disk image, the script trashes the image at the same time. I use this a lot with downloaded application distribution images.
I run the script with FastScripts with the keyboard short cut Cmd-Option-E. Unfortunately, there seems to be a Finder bug that makes the script take a very long time do finish.
set myVolumes to getSelectedVolumes()
if (count myVolumes) < 1 then return
set myImages to getImageList()
repeat with myVolume in myVolumes
set myImagePath to ""
set myImageFile to null
repeat with myImage in myImages
repeat with myEntity in |system-entities| of myImage
try
set mountPoint to |mount-point| of myEntity
displayed name of myVolume
if mountPoint = ("/Volumes/" & displayed name of myVolume) then
set myImagePath to |image-path| of myImage
set myImageFile to POSIX file myImagePath
end if
end try
end repeat
end repeat
tell application "Finder"
eject myVolume
if myImageFile is not null then
try
delete file myImageFile
end try
end if
end tell
end repeat
on getSelectedVolumes()
set volumeList to {}
tell application "Finder"
set mySelection to selection
repeat with myItem in mySelection
set myItem to contents of myItem
-- log class of myItem
if kind of myItem is "Volume" then
set end of volumeList to myItem
end if
end repeat
end tell
return volumeList
end getSelectedVolumes
on getImageList()
set diskinfo to do shell script "hdiutil info -plist"
tell application "System Events"
set diskinfo to value of (make new property list item with properties {text:diskinfo})
set imageList to |images| of diskinfo
end tell
return imageList
end getImageList
Recent versions of the Sparkle auto-update framework used by many Mac OS X applications require the publisher to protect their users from malicious attacks by either delivering application updates over SSL or digitally signing them, which is a good thing.
Signing involves a few steps with OpenSSL that need to be executed again for every release build. To make this more reliable and convenient, I automated the process with a custom script phase in my “distribution” Xcode target.
The first step is to generate a DSA key pair as documented on the Sparkle website. This gives you the private key in dsa_priv.pem and the public key in dsa_pub.pem.
The private key is sensitive and I did not want it lying around on the file system, so I added it to my keychain as a Secure Note in the Keychain Access utility:


For completeness, I also added the public key even though it is not sensitive, resulting in these two secure note items:

I deleted the private key from the file system, moved the public key to the resources of my Xcode project and included its name in the Info.plist file under the SUPublicDSAKeyFile key, again as documented on the Sparkle Website.
In Xcode, I added a “Distribution” shell script target:

This is the shell script code:
set -o errexit
[ $BUILD_STYLE = Release ] || { echo Distribution target requires "'Release'" build style; false; }
VERSION=$(defaults read "$BUILT_PRODUCTS_DIR/$PROJECT_NAME.app/Contents/Info" CFBundleVersion)
DOWNLOAD_BASE_URL="http://www.example.com/download/"
RELEASENOTES_URL="http://www.example.com/software/my-cool-app/release-notes.html#version-$VERSION"
ARCHIVE_FILENAME="$PROJECT_NAME $VERSION.zip"
DOWNLOAD_URL="$DOWNLOAD_BASE_URL/$ARCHIVE_FILENAME"
KEYCHAIN_PRIVKEY_NAME="Sparkle Private Key 1"
WD=$PWD
cd "$BUILT_PRODUCTS_DIR"
rm -f "$PROJECT_NAME"*.zip
zip -qr "$ARCHIVE_FILENAME" "$PROJECT_NAME.app"
SIZE=$(stat -f %z "$ARCHIVE_FILENAME")
PUBDATE=$(date +"%a, %d %b %G %T %z")
SIGNATURE=$(
openssl dgst -sha1 -binary < "$ARCHIVE_FILENAME" \
| openssl dgst -dss1 -sign <(security find-generic-password -g -s "$KEYCHAIN_PRIVKEY_NAME" 2>&1 1>/dev/null | perl -pe '($_) = /"(.+)"/; s/\\012/\n/g') \
| openssl enc -base64
)
[ $SIGNATURE ] || { echo Unable to load signing private key with name "'$KEYCHAIN_PRIVKEY_NAME'" from keychain; false; }
cat <<EOF
<item>
<title>Version $VERSION</title>
<sparkle:releaseNotesLink>$RELEASENOTES_URL</sparkle:releaseNotesLink>
<pubDate>$PUBDATE</pubDate>
<enclosure
url="$DOWNLOAD_URL"
sparkle:version="$VERSION"
type="application/octet-stream"
length="$SIZE"
sparkle:dsaSignature="$SIGNATURE"
/>
</item>
EOF
echo scp "'$HOME/svn/my-cool-app/build/Release/$ARCHIVE_FILENAME'" www.example.com:download/
echo scp "'$WD/appcast.xml'" www.example.com:web/software/my-cool-app/appcast.xml
You have to set the shell to /bin/bash because this code uses features that are not available otherwise.
This script:
- enforces a Release build
- creates a .zip file of the application. You could also package up a .dmg with additional material.
- fetches the signing private key from the keychain. This step might pop up a dialog:
- calculates the SHA1 checksum of the distribution file
- signs the checksum with the private key
- converts the signature to Base64
- emits an
itemblock with all the information (date, size, version) about the update. You add this block to your appcast XML file.
I also let it print out the scp commands required to publish the update.
When I work on Cocoa programs I read the framework documentation with Xcode’s built in documentation viewer, but sometimes I still open the HTML page for a particular class in Safari with LaunchBar because it is very quick (see this older post).
Xcode downloads these documentation sets from the Internet, and unfortunately all of the thousands of files in there are tagged with the “quarantine” extended file system attribute. This means that the system warns you about potentially harmful content every time you open one of the .html files outside of Xcode, such as in the LaunchBar case.
To get around this, you can clear the quarantine attribute on all files with this command:
find /Developer/Documentation -type f -print0 | xargs -n 100 -0 sudo xattr -d com.apple.quarantine
Here’s another trick to speed up file system navigation in the shell. A previous tip showed how to use bookmarks to reach deeply nested directories effectively.
I use that a lot, but I frequently use it to enter directories with a large number of subdirectories whose names are long and share a prefix:
... Arteria-Media-ImageEditor Arteria-Media-ImageEditor-Base Arteria-Media-ImageRep-Magick Arteria-Media-JSONClient Arteria-Media-JSONSegmenter Arteria-Media-Webapp-MediaServer ...
I wanted a way to enter one of these directories by typing only a minimal, unique substring of its name (like typing *substring* but only considering directories and ignoring case), so I wrote this shell function:
function c {
shopt -q nocasematch || resetcase=1
shopt -s nocasematch
for i in *; do [ -d "$i" ] && [[ "$i" == *"$1"* ]] && cd "$i" && break; done
[ $resetcase ] && shopt -u nocasematch
}
Now I can type
c ncl
and, in the example directory above, it takes me directly into the Arteria-Media-JSONClient subdirectory.
A while ago I published a Finder toolbar AppleScript called “Open Terminal Here” to open a terminal window in the directory displayed by the frontmost Finder window or the Finder selection or the files/folders dropped onto the icon.
However, these days I find myself using a similar script triggered by a keyboard shortcut much more often. I assigned it to Cmd-T which is very convenient and quick. As always, I use FastScripts to do the keyboard launching.
Here’s the script:
tell application "Finder" set sel to selection if (count sel) > 0 then my openTerminal(item 1 of sel) else my openTerminal(target of window 1) end if end tell on openTerminal(location) set location to location as alias set the_path to POSIX path of location repeat until the_path ends with "/" set the_path to text 1 thru -2 of the_path end repeat tell application "Terminal" activate do script "cd " & quoted form of the_path & " && echo $'\\ec'" end tell end openTerminalOpen this script in Script Editor
Just finished William Gibson’s latest novel, Spook Country. I liked it but if you plan to read it you probably should read “Pattern Recognition” first.
Back in 2005 I published a little shell script called “term”. It clones the current Mac OS X Terminal session (working directory) into a new window, or executes a command in a new window. See the old posting for some usage examples.
Here’s a new version that adds a -t option to open a new tab instead of a new window and the ability to specify window position and size:
#!/bin/sh
#
# Open a new Mac OS X terminal window or tab in the current or another
# directory and optionally run a command in the new window or tab.
#
# - Without any arguments, the new terminal window opens in
# the current directory, i.e. the executed command is "cd $PWD".
# - If the first argument is a directory, the new terminal will "cd" into
# that directory before executing the remaining arguments as command.
# - The optional "-t" flag executes the command in a new tab
# instead of a new window.
# - The optional "-x" flag closes the new window or tab
# after the executed command finishes.
# - The optional "-p" flag takes an argument of the form x,y (e.g. 40,50) and
# positions the terminal window to the indicated location on the screen
# - The optional "-s" flag takes an argument of the form w,h (e.g. 800,400) and
# resizes the terminal window to the indicated width and height in pixels.
#
# Written by Marc Liyanage <http://www.entropy.ch>
#
# Version 2.1
#
set -e
while getopts xtp:s: OPTION; do
[ $OPTION = "x" ] && { EXIT='; exit'; }
[ $OPTION = "t" ] && { TAB=1; }
[ $OPTION = "p" ] && { POSITION="set position of window 1 to {$OPTARG}"; }
[ $OPTION = "s" ] && { SIZE="set size of window 1 to {$OPTARG}"; }
done
for (( $OPTIND; $OPTIND-1; OPTIND=$OPTIND-1 )); do shift; done
if [[ -d "$1" ]]; then WD=$(cd "$1"; pwd); shift; else WD=$PWD; fi
COMMAND="cd '$WD' && echo -n \$'\\\\ec';"
for i in "$@"; do
COMMAND="$COMMAND '$i'"
done
if [ $TAB ]; then
osascript 2>/dev/null <<EOF
tell application "System Events"
tell process "Terminal" to keystroke "t" using command down
end
tell application "Terminal"
activate
do script with command "$COMMAND $EXIT" in window 1
$POSITION
$SIZE
end tell
EOF
else
osascript <<EOF
tell application "Terminal"
activate
do script with command "$COMMAND $EXIT"
$POSITION
$SIZE
end tell
EOF
fi
I can’t quite remember where I read the tip about this book, it must have been some blog.
Although not all essays are of the same quality, it’s a good and fun read overall...
Compiled by Heather B. Armstrong, award-winning publisher and uber-mistress of the phenomenally popular dooce.com®, this hilarious and heartwarming celebration of "everything dad" features original stories from some of the country's most celebrated bloggers, including Alice Bradley (Finslippy) Doug French (Laid Off Dad), Maggie Mason (Mighty Girl), Matthew Baldwin (Defective Yeti), Sarah Brown (Que Sera Sera), and more.
From a new father's comparison of pregnancy to Tolkien's Lord of the Rings, to a mother's story of bravely surviving a husband-son infatuation with Star Wars, to the mini triumphs and tragedies of toddlerhood, this book provides a unique, no-holds-barred glimpse into the quirks and candid moments of modern dads.
Whether we relish or fear growing up to be like our fathers...whether we've inherited his nose, sense of humor, or entire value system, our dads loom large in who we are and the choices we make. Things I Learned about my Dad in Therapy touches upon the many joys and discoveries of fatherhood, one essay at a time.
I often download huge files that I only need temporarily, things like installer packages and disk images. They end up on my desktop because that is my download folder and quite often they also end up in the Time Machine backup, wasting a lot of space there.
I wanted a contextual menu command to exclude these files from the backup in the Finder so I wrote this AppleScript:
on main(filelist)
set target to quoted form of (POSIX path of (item 1 of filelist as alias))
do shell script "xattr -w com.apple.metadata:com_apple_backup_excludeItem com.apple.backupd " & target
end main
It adds extended attributes to the file telling Time Machine to ignore it. I got this information from an excellent article about Time Machine.
To run the script as contextual menu in the Finder I use Ranchero Software’s free Big Cat utility. After installing that and storing my script in $HOME/Library/Application Support/Big Cat Scripts/Files, I have a new entry in the Finder contextual menu for files:

The only problem is that custom contextual menu items are in a “More” submenu on Leopard, which moves the Big Cat scripts down to the inconvenient third level.
A coworker just showed me the useful UNIX tool cdargs. It provides bookmarks for often used directories in the shell.
You can do something similar with shell aliases and pushd/popd, but I find this far more convenient.
It doesn’t seem to be in MacPorts, so I downloaded, compiled and installed the source package with these commands:
$ curl -s http://www.skamphausen.de/software/cdargs/cdargs-1.35.tar.gz | tar -xzf - $ cd cdargs-1.35/ $ ./configure && make $ sudo make install Password: $ cp contrib/cdargs-bash.sh /usr/local/bin/ $ echo ". cdargs-bash.sh" >> ~/.bashrc
Reopen the terminal window and you’ll get two new commands:
ca- creates a bookmark of the current directory
cv- displays the list of bookmarks and changes the directory if you pick one.
Here’s cdargs in action:
A bookmark consists of a directory path and a shortcut word. ca without argument uses the current directory name as shortcut. Sometimes that’s not what you want so you can explicitly choose the shortcut by passing it as argument:
ca arteria
cv without argument shows a list of all bookmarks and lets you choose a directory to change to. With a shortcut as argument, it changes immediately to that directory. This usage supports completion of the shortcut so you only have to type an unambiguous prefix and hit the tab and return keys.
The list view can do a few more things, for example
- it supports vi-style h/j/k/l navigation
- you can drill down into a bookmarked directory with the arrow or “l” key to enter some subdirectory directly
- typing “v” edits the list in your text editor of choice
- typing “d” deletes a bookmark from the list
Update 2008-08-31: I just submitted a port for cdargs to MacPorts, hopefully you can soon get it from there.
This is starting to look like a BBEdit blog :-) Can you tell I spend most of my working hours in this program... I guess I should have a category for it.
Anyway, a few weeks ago I posted a script to move BBEdit’s palettes to the screen edge. After a while, I got tired of executing it manually. My screen resolution usually changes when the machine wakes from sleep, so I looked for utilities that run AppleScripts on wakeup and found the fantastic SleepWatcher, a command line tool that does exactly what I want.
I installed it, along with its startup item. Then I created a .wakeup file in my home directory, made it executable and put this in it:
#!/bin/sh ps ax | grep -v grep | grep -q BBEdit && osascript ~/Library/Scripts/Applications/BBEdit/Move\ Palettes\ to\ Screen\ Edge.scpt
The other situation where the script should run automatically is when BBEdit is launched, because it is likely that the screen resolution was different when it quit. BBEdit remembers the palette positions and puts them in the wrong spot.
Fortunately, BBEdit can do exactly that. It runs scripts in the $HOME/Library/Application Support/BBEdit/Startup Items folder when it starts so I put an alias pointing to the script there and it works perfectly.
![]() |
|


















