Word Cloud Obsession Whale

Ok, I told the world and myself (in a podcast you haven’t heard yet1) that I wouldn’t admit to putting time into this, but… I kinda like it. Given that you’ll probably need it at least once at some point, I might as well share it and save you the time.

This is a bash function called bid (bundle id) that grabs an application ID (strings like com.brettterpstra.marky) for use with things like AppleScript, Cocoa and command line scripting where it’s safer to stick with a bundle ID than an application name. It’s also perfect for defaults read/write commands. Even for locating preference files you want to trash…

bid takes one argument (well, it combines all arguments into one, more on that in a second) and uses Spotlight2 to find the closest matching Application name to the text you give it. For the sake of speed, it only searches in:

  • /Applications
  • ~/Applications
  • /Developer/Applications

The search within those folders is deep, though, so apps that live inside of a folder hierarchy (Adobe) or .localized versions will be picked up. It builds a list of every application in those folders and passes it to awk 3. The search string you pass to bid is turned into a regular expression where spaces in the query will match any string. So “scr ner” will find Scrivener.app, and “tw r” will find “Twitter.app.”4

It currently maintains the default sort order that mdfind returns for this query, which is alphabetical by folder then file. It also uses head to chop off the first result to return. Thus, it will always return the first matching file, not necessarily the most likely one for a fuzzy string. It’s too time consuming to sort them more accurately and, at least on my system, this hits even the fuzziest string on the first try 90%5 of the time.

So, throw this in your .bash_profile and next time you need a Bundle ID type bid partial app title and revel in the magic.6

bid.shraw
"
bid() {
	local shortname location

	# combine all args as regex
	# (and remove ".app" from the end if it exists due to autocomplete)
	shortname=$(echo "${@%%.app}"|sed 's/ /.*/g')
	# if the file is a full match in apps folder, roll with it
	if [ -d "/Applications/$shortname.app" ]; then
		location="/Applications/$shortname.app"
	else # otherwise, start searching
		location=$(mdfind -onlyin /Applications -onlyin ~/Applications -onlyin /Developer/Applications 'kMDItemKind==Application'|awk -F '/' -v re="$shortname" 'tolower($NF) ~ re {print $0}'|head -n1)
	fi
	# No results? Die.
	[[ -z $location || $location = "" ]] && echo "$1 not found, I quit" && return
	# Otherwise, find the bundleid using spotlight metadata
	bundleid=$(mdls -name kMDItemCFBundleIdentifier -r "$location")
	# return the result or an error message
	[[ -z $bundleid || $bundleid = "" ]] && echo "Error getting bundle ID for \"$@\"" || echo "$location: $bundleid"
}
  1. This is not why the podcast is a bit behind schedule. This was before I recorded it. It’s late because I’m sick and I have a day job. I still love you and hope you’ll welcome it with open arms. 

  2. I initially just added this to my existing app name tab-completion scripts before deciding to make it a custom Spotlight search every time. I honestly don’t remember why I decided that wasn’t just good enough, but apparently I did. I should log these OC bash scripting sessions in more detail for future study and diagnosis of my mental illness. 

  3. Yes, I played with including the argument text as part of the query string but actually found the response time slower than just pulling “kind:” and running it through awk or grep. Go figure. 

  4. There’s a point where you realize you’re just doing things because you can. Fuzzy searching was that point for me. This wasn’t a case that really needed it, so I stopped trying to implement it in Bash when my eyes started bleeding. 

  5. Not statistically proven. 

  6. Not real magic. You can tell by the number of footnotes on this post that the brain power expended was far out of balance with the utility of the script.