June 5, 2008 at 7:00 PM by Dr. Drang
In an earlier post I discussed the Mac’s
addressbook URI scheme and how you can open a particular contact in your Address Book with a command like
from the Terminal. Everything after the double slash is the ID of the contact, accessible via the
id property in AppleScript. In a similar way, you can create links in HTML documents which, when clicked, will open Address Book to the contact with that ID. (On my work computer, the above ID is for Apple, Inc.; the ID for Apple on your computer will be different.) I’ve been using such links a lot recently--for a serverless wiki-like system I’ll be writing about soon--and needed a utility for quickly extracting the ID of an Address Book contact.
The Address Book opener described in that earlier post used a pair of scripts--one AppleScript and one bash script. I never liked that setup, because the AppleScript was somewhat convoluted and because it used two script to do basically one thing. So even though the ID extraction script would be almost identical to the Address Book opener, I didn’t want to reuse my old code.
The script works like this: entering
abid john smith
at the Terminal prints the ID of the first contact with the names “john” and “smith.” Any number of arguments can be given to the
abid command; the name of the contact must have each of the arguments as a substring. The search through the Address Book is case-insensitive. If no match is found,
abid prints “No match!” instead of the ID.
Here’s the code. It’s written in Python using the appscript module.
1: #!/usr/bin/python 2: 3: import sys 4: from appscript import * 5: 6: # Find the contacts that have all the names in the given list. 7: def searchName(nameList): 8: names = [ x.lower() for x in nameList ] 9: # Get everyone whose last name matches. 10: matches = [ x for x in app('Address Book').people.get() if names[-1] in x.name.get().lower() ] 11: # Look for matches with the other names if there are any. 12: while len(names) > 1: 13: del names[-1] 14: matches = [ x for x in matches if names[-1] in x.name.get().lower() ] 15: # Return the list of matches. 16: return matches 17: 18: # Print the ID of the top match. Or print an error message. 19: try: 20: print searchName(sys.argv[1:]).id.get().replace(':', '%3A') 21: except IndexError: 22: print "No matches!"
searchName function does almost the same thing as the identically-named function in my old AppleScript, but is much shorter and the logic is cleaner. Which is why I wanted to get away from AppleScript. I suppose the really cool, Lispy way to write
searchName would be to make it recursive instead of iterative, but the while loop works fine.
name of a contact is the full name--prefix, first, middle, last, suffix--as a string. If the contact is a company, the
name is the company name. The list comprehensions in Lines 10 and 14 are basically substring filters.
You can see in Line 20 that I’ve URL-encoded the colon near the end of the ID, replacing it with %3A. The encoding isn’t necessary to make the URL legal, but eliminating that colon from the string makes the ID easier to use in the serverless wiki I mentioned at the top of this post.
The program works through the argument list from back to front instead of front to back. I expected to give it the names in “first last” order, and I thought that filtering by last name first would be faster because identical last names are rarer than identical first names and the list comprehension in the while loop would therefore be working with smaller lists. This notion could be completely off base; I haven’t done any benchmarking.
There’s no need to pass
abid more than one name if one of the contact’s names is unique. I have only one Adolf in my Address Book, so
is all I need to get his ID. Nicknames will work if the nickname is a subset of the name given in Address Book. For example, if someone is listed as Robert Johnson, then
abid rob johnson
will find him, but
abid bob johnson
This script would likely be unnecessary if Address Book would show us the ID and let us copy it into the clipboard. But I haven’t found any way to get Address Book to show us the ID; if you know of a way, I’d like to hear about it.