How can I add OS X "tags" to files programmatically?

nacross picture nacross · Nov 1, 2013 · Viewed 17.3k times · Source

Since Mavericks, OS X has had the ability to tag & colour files in Finder.

finder tags

Is there any way to add tags to files through Cocoa APIs or via a shell command?

Answer

beroe picture beroe · Nov 6, 2013

Sorry for adding another answer, but the one related to setting Label colors was pretty long already. Here is an excerpt from a python script that I use to set the User Tags. It seems to work to make things searchable, but not sure if the tags will show up correctly. Usage is basically:

tagfile.py "Tag Name" FileOrFolderName

Code below.

#! /usr/bin/env python
# -*- coding: utf-8 -*-

""" Write tags to file
Usage:
    tagfile.py "TagName" FileName1 FileName2 

    You can use wildcards for the file name. Use quotes if spaces in tags.
    To check if it worked, use xattr -l FileName

"""

import sys
import subprocess

def writexattrs(F,TagList):
    """ writexattrs(F,TagList):
    writes the list of tags to three xattr fields on a file-by file basis:
    "kMDItemFinderComment","_kMDItemUserTags","kMDItemOMUserTags
    Uses subprocess instead of xattr module. Slower but no dependencies"""

    Result = ""

    plistFront = '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><array>'
    plistEnd = '</array></plist>'
    plistTagString = ''
    for Tag in TagList:
        plistTagString = plistTagString + '<string>{}</string>'.format(Tag.replace("'","-"))
    TagText = plistFront + plistTagString + plistEnd

    OptionalTag = "com.apple.metadata:"
    XattrList = ["kMDItemFinderComment","_kMDItemUserTags","kMDItemOMUserTags"]
    for Field in XattrList:    
        XattrCommand = 'xattr -w {0} \'{1}\' "{2}"'.format(OptionalTag + Field,TagText.encode("utf8"),F)
        if DEBUG:
            sys.stderr.write("XATTR: {}\n".format(XattrCommand))
        ProcString = subprocess.check_output(XattrCommand, stderr=subprocess.STDOUT,shell=True) 
        Result += ProcString
    return Result

DEBUG = False


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print __doc__
    else:
        TagList = [ sys.argv[1] ]
        # print TagList
        # Or you can hardwire your tags here
        # TagList = ['Orange','Green']
        FileList = sys.argv[2:]

        for FileName in FileList:
            writexattrs(FileName, TagList)