I’ve actually begun work on a book about tagging, and Mavericks in particular. It’s a subject I truly enjoy, and so far it’s kind of been writing itself. I hope to wrap it up faster than some of my other side projects. Bits that I think aren’t mass-consumable enough for publication will end up here, where my gentle readers can decide for themselves if they want to do anything with it.

One such bit is a reminder that mdfind can search tags just like Spotlight (tag:tagname). A simple Terminal command such as mdfind -onlyin ~ 'tag:habanero AND tag:cooking' will return all your spicy recipes in a list. There are a lot of possibilities for scripting complex tag functionality. I’ve done a lot with mdfind and OpenMeta in the past, but I’ve decided to start integrating tagging into my command line life a bit more. To start, I wrote a quick shell function as a shortcut to find files in the current directory based on tags:

listtags.bashraw
"
# Updated 2013-10-31 to handle:
# listing all files with tags
# list only files without tags
# and deal with spaces in filenames (that part got ugly).

# Also, set the query variable to local

# List files with specified Finder tags in current directory and subdirectories
# Works with partial words starting from the beginning of the word
lst() {
	local query
	# if the first argument is "all" (case insensitive),
	# a boolean AND search will be used. Defaults to OR.
	bool="OR"
	[[ $1 =~ "all" ]] && bool="AND" && shift

	# if there's no argument or the argument is "+"
	# list all files with any tags
	if [[ -z $1 || $1 == "+" ]]; then
		query="kMDItemUserTags == '*'"
	# if the first argument is "-"
	# list only files without tags
	elif [[ $1 == "-" ]]; then
		query="kMDItemUserTags != '*'"
	# Otherwise, build a Spotlight syntax query string
	else
		query="tag:$1"
		shift
		for tag in $@; do
			query="$query $bool tag:$tag"
		done
	fi

	while IFS= read -r -d $'\0' line; do
		echo ${line#`pwd`/}
	done < <(mdfind -onlyin . -0 "$query")
}

When you run it, you can give it a single tag or a space-separated list of tags. By default, it will list every file that has any of the listed tags (boolean OR search), but if you make the first argument the word “and”, it will turn it into a search that will only list files that contain ALL the tags listed.

Accidental discoveries and new possibilities

I noticed something interesting while playing with this. With both this script and in Spotlight, you can use just the first few letters of a tag, if they’re enough to make it unique within the folder. Partials have to be the start of the tag, unless there’s a non-letter character in the tag. Punctuation splits the queried term and you can match from the beginning of anything immediately following the non-alphabet character. Think @, #, :, and other symbols that have become standards for representing tags, categories, projects and contexts.

If I have a tag called coding:marked, I can turn up results with tag:marked, tag::marked and tag:coding. Of course, coding and marked mix in with other results that actually start with those words, but being able to search for :marked and separate it from marked is actually intriguing.

This led me to thinking about combining tags to semantically categorize a file and still make it searchable in larger groups, trimming down the number of tags on each file. The more I thought about it, the more I couldn’t really see the point of not just putting the two tags on the file separately. But… it does open the door to the possibility of “tag groups,” a hierarchical taxonomy that can be searched in various ways. I have to think about that one a bit more.