Since Mavericks, OS X has had the ability to tag & colour files in Finder.
Is there any
Check out tag, "a command line tool to manipulate tags on Mac OS X 10.9 Mavericks files, and to query for files with those tags". The GitHub repository has installation instructions (there are Homebrew and MacPorts packages).
This does not cover tags, but for changing label colors, one way to do it is through a command like this:
xattr -wx com.apple.FinderInfo \
0000000000000000000400000000000000000000000000000000000000000000 myfile.txt
The 04
buried in the middle is setting the file color.
Here is a python script which wraps that command lets you set the tag color on a file or series of files:
import sys
import subprocess
def colorizeFile(ColorName,FileName):
ReverseTable = {
"clear" : "01",
"gray" : "03",
"green" : "04",
"purple" : "06",
"blue" : "09",
"yellow" : "0A",
"red" : "0C",
"orange" : "0E",
"c" : "01",
"a" : "03",
"g" : "04",
"p" : "06",
"b" : "09",
"y" : "0A",
"r" : "0C",
"o" : "0E",
}
HexString = 18*"0" + ReverseTable.get(ColorName) + 44*"0"
Xcommand = 'xattr -wx com.apple.FinderInfo {0} {1}'.format(HexString,FileName)
ProcString = subprocess.check_call(Xcommand, stderr=subprocess.STDOUT,shell=True)
if __name__ == "__main__":
if len(sys.argv)<3:
sys.stderr.write(__doc__.format(sys.argv[0]))
else:
Cname = sys.argv[1]
Flist = sys.argv[2:]
for File in Flist:
colorizeFile(Cname.lower(),File)
sys.stderr.write("## Colorized {0} file(s) as {1}\n".format(len(Flist),Cname))
Usage is:
labelcolor.py [color] *.jpg
where [color] is a name or abbreviation as defined below:
clear (c), grAy (a), green (g), purple (p),
blue (b), yellow (y), red (r), orange (o)
I add this answer, because OP asked for a shell script and tagged it bash. I wrote this Automator service, which tags the selected file with the tags of another file. I have added comments to outline the use of bash's interaction with the tags and colours using bash script.
In scripts both OpenMeta and Mavericks tags can be accessed with the command xattr. Using it without modifiers, $ xattr [file]
, gives a list of set attributes. $ xattr -h
gives a nice guide to usage.
Mavericks' tags are in com.apple.metadata:_kMDItemUserTags, while OpenMeta tags can be in a variety of attributes. Amongst others com.apple.metadata:kOMUserTags
, org.openmetainfo:kMDItemOMUserTags
and org.openmetainfo:kOMUserTags
.
Mavericks handles colours and tags in different attributes, by placing tags in _kMDItemUserTags and colours in FinderInfo for every file. This is a bizarre choice, and it is one of the reasons Finder struggles under the pressure of tagging. If you have 800 files tagged kapow, each in a different folder, and you subsequently choose the colour blue for kapow, Finder has to find and alter attributes for every single file.
You can play around with the oddity by removing the com.apple.FinderInfo attribute from a tagged and coloured file: $ xattr -d com.apple.FinderInfo [file]
. The colour will disappear in Finder listings, but the tag (and its colour) remains associated with the file.
In the script, the selected file(s) in Finder is/are saved to the variable $tagless, and the chosen supplier of tags is $tagfull.
TAGFULID=${#@}
TAGFUL=${!TAGFULID}
## Use xattr to read all existing tags:
ATTRS=$(xattr "$TAGFUL")
for f in "$@" ## For every selected file in Finder, do:
do
if("$TAGFUL"="$f") ## Is the supplier of tags is amongst the selected files?
then
break
fi
if [[ "$ATTRS" == *kMDItemUserTags* ]] ## Are there tags?
then
## Load tags:
TAGS=$(xattr -px com.apple.metadata:_kMDItemUserTags "$TAGFUL")
## Write tags:
xattr -wx com.apple.metadata:_kMDItemUserTags "$TAGS" "$f"
fi
if [[ "$ATTRS" == *FinderInfo* ]] ## Are there colours?
then
## Load colour:
FINDERINFO=$(xattr -px com.apple.FinderInfo "$TAGFUL")
## Write colour:
xattr -wx com.apple.FinderInfo "$FINDERINFO" "$f"
fi
done
In Apple's What's New in OS X it states that NSURL
handles tags, and the Common File System Resource_Keys gives the required key as NSURLTagNamesKey
and states its value is just an array of strings.
Starting with Mavericks, it is possible to get and set color tags in Cocoa, using NSURL
.
NSURL
has a slew of properties that can be set or read, through the respective setResourceValue:forKey:error:
and getResourceValue:forKey:error:
methods.
Using the NSURLLabelNumberKey
key, you can set the color tags, as follows:
NSURL *fileURL = [NSURL fileURLWithPath:@"/Users/[username]/Documents/[some_file]"];
NSError *resourceError;
if (![fileURL setResourceValue:@(2) forKey:NSURLLabelNumberKey error:&resourceError]) {
NSLog(@"Error while setting file resource: %@", [resourceError localizedDescription]);
}
If this is executed on a file that only has one color, then it clears the current color, and sets the specified color. However, if multiple colors are already set on the file, then it does not clear the existing colors before setting the specified color.
Here is the value-color mapping (on El Capitan):
I have not been able to set a tag using NSURLLabelColorKey
. Here is my experience on El Capitan, with the keys related to 'tags' (Colors):
NSURLLabelNumberKey
: can be read/set successfully, with numbers 0-7. Any other number will return an error. If there are multiple tags set, then this will return the index of the first color that is set, as it searches numerically through the indexes 1 through 7. Although you can clear a color in Finder by clicking on the color, programmatically setting a color that is already set does not clear that color.NSURLLabelColorKey
: returns nil, even when a color tag is set for a file. Setting a value with this key has no effect.NSURLTagNamesKey
: returns an array of the color names for the tags that are set.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)