Since Mavericks, OS X has had the ability to tag & colour files in Finder.
Is there any way to add tags to files through Cocoa APIs or via a shell command?
Since Mavericks, OS X has had the ability to tag & colour files in Finder.
Is there any way to add tags to files through Cocoa APIs or via a shell command?
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)
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).
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.
The OpenMeta framework is a third-party standard for adding metadata to OS X files using extended attributes. It is used by a number of third-party applications.
Or you can use the XATTR command to manipulate the extended attributes via command line.
You could give this a shot:
xattr -w com.apple.metadata:_kMDItemUserTags '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><array><string>Orange</string><string>Red</string></array></plist>' $currentFile
You'll want to replace $currentFile with the file you'd like to add tags to, and change
<string>Orange</string><string>Red</string>
to a list of whatever tags you want to add.
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)
With multiple answers, one of which is accepted:
Here in Stack Overflow the question arose slightly earlier (2013-11-01) so I'll add my answer here.
Open source at https://code.google.com/p/openmeta/source/browse/trunk/trunk/openmeta
The openmeta
command appears to take a dual attribute approach, working with both:
com.apple.metadata:kMDItemOMUserTags
com.apple.metadata:_kMDItemUserTags
sh-3.2$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.9.5
BuildVersion: 13F1096
sh-3.2$ uname -a
Darwin gpes3e-gjp4.local 13.4.0 Darwin Kernel Version 13.4.0: Wed Mar 18 16:20:14 PDT 2015; root:xnu-2422.115.14~1/RELEASE_X86_64 x86_64
sh-3.2$ date
Sun 26 Jul 2015 08:00:23 BST
sh-3.2$ rm ~/Desktop/test.txt
sh-3.2$ touch ~/Desktop/test.txt
sh-3.2$ xattr -l ~/Desktop/test.txt
sh-3.2$ ./openmeta
openmeta version 0.1 by Tom Andersen code.google.com/p/openmeta/
Usage: openmeta [options] -p PATH[s]
Note that commas are to be used nowhere - tag lists use quotes for two word tags in output
example (list tags and ratings): openmeta -p PATH
example (list tags and ratings multiple): openmeta -p PATH PATH
example (list tags): openmeta -t -p PATH[s]
example (add tags): openmeta -a foo bar -p PATH[s]
example (add tags with spaces): openmeta -a "three word tag" "foo bar" -p PATH[s]
example (set tags): openmeta -s foo bar -p PATH[s]
example (clear all tags): openmeta -s -p PATH[s]
example (set managed): openmeta -m Y -p PATH[s]
example (set rating 0 - 5 stars): openmeta -r 3.5 -p PATH[s]
example (print rating): openmeta -r -p PATH[s]
example (clear rating): openmeta -r 0.0 -p PATH[s]
example (lousy rating): openmeta -r 0.1 -p PATH[s]
sh-3.2$ ./openmeta -a kerfuffle -p ~/Desktop/test.txt
kerfuffle /Users/gjp22/Desktop/test.txt
sh-3.2$ ./openmeta -p ~/Desktop/test.txt
/Users/gjp22/Desktop/test.txt
tags: kerfuffle
rating: none found
sh-3.2$ xattr -l ~/Desktop/test.txt
com.apple.metadata:kMDItemOMUserTagTime:
00000000 62 70 6C 69 73 74 30 30 33 41 BB 64 BD 3C D4 95 |bplist003A.d.<..|
00000010 F2 08 00 00 00 00 00 00 01 01 00 00 00 00 00 00 |................|
00000020 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 11 |..|
00000032
com.apple.metadata:kMDItemOMUserTags:
00000000 62 70 6C 69 73 74 30 30 A1 01 59 6B 65 72 66 75 |bplist00..Ykerfu|
00000010 66 66 6C 65 08 0A 00 00 00 00 00 00 01 01 00 00 |ffle............|
00000020 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 14 |......|
00000036
com.apple.metadata:_kMDItemUserTags:
00000000 62 70 6C 69 73 74 30 30 A1 01 5B 6B 65 72 66 75 |bplist00..[kerfu|
00000010 66 66 6C 65 0A 30 08 0A 00 00 00 00 00 00 01 01 |ffle.0..........|
00000020 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 16 |........|
00000038
kOM109SyncDone:
00000000 62 70 6C 69 73 74 30 30 09 08 00 00 00 00 00 00 |bplist00........|
00000010 01 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 09 |..........|
0000002a
sh-3.2$
Apple Finder, for example.
After using Finder to remove the kerfuffle tag, kerfuffle remains as an OpenMeta tag:
sh-3.2$ date ; xattr -l ~/Desktop/test.txt
Sun 26 Jul 2015 08:02:13 BST
com.apple.metadata:kMDItemOMUserTagTime:
00000000 62 70 6C 69 73 74 30 30 33 41 BB 64 BD 3C D4 95 |bplist003A.d.<..|
00000010 F2 08 00 00 00 00 00 00 01 01 00 00 00 00 00 00 |................|
00000020 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 11 |..|
00000032
com.apple.metadata:kMDItemOMUserTags:
00000000 62 70 6C 69 73 74 30 30 A1 01 59 6B 65 72 66 75 |bplist00..Ykerfu|
00000010 66 66 6C 65 08 0A 00 00 00 00 00 00 01 01 00 00 |ffle............|
00000020 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 14 |......|
00000036
com.apple.metadata:_kMDItemUserTags:
00000000 62 70 6C 69 73 74 30 30 A0 08 00 00 00 00 00 00 |bplist00........|
00000010 01 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 09 |..........|
0000002a
kOM109SyncDone:
00000000 62 70 6C 69 73 74 30 30 09 08 00 00 00 00 00 00 |bplist00........|
00000010 01 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 09 |..........|
0000002a
sh-3.2$
With attention to domains and naming conventions: Developer thoughts on adopting OpenMeta – Ironic Software (2009-03, and now in the Internet Archive Wayback Machine) reminds us that com.apple.metadata
was for use by Apple when OpenMeta (a project not in the apple.com
domain) began the Apple-oriented com.apple.metadata:kMDItemOMUserTags
approach.
So I should not expect Apple software to gain or maintain compatibility with both approaches to tagging.
In some cases it may be desirable to remove Apple-oriented com.apple.metadata:_kMDItemUserTags
tags without removing OpenMeta-oriented com.apple.metadata:kMDItemOMUserTags
tags.
However, doing so – programmatically – is probably beyond the scope of the question asked by @nacross.
Starting with Mavericks, it is also possible to set colors tags in Cocoa, using NSAppleScript
.
NSURL *fileURL = [NSURL fileURLWithPath:@"/Users/sheaparis/Documents/filezilla_sites.xml"];
//Format the filepath for the AppleScript environment.
// Without this, the file cannot be found.
NSString *filepath = [fileURL path];
NSString *appleScriptFilePath = [filepath stringByReplacingOccurrencesOfString:@"/" withString:@":"];
if ([appleScriptFilePath hasPrefix:@":"]) {
appleScriptFilePath = [appleScriptFilePath substringFromIndex:1];
}
NSLog(@"appleScriptFilePath: %@", appleScriptFilePath);
//Tells Finder to set the Red color tag for the specified file
NSString *sourceString = [NSString stringWithFormat:
@"set theFile to \"%@\" as alias\n"
"tell application \"Finder\" to set label index of theFile to 2", appleScriptFilePath];
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:sourceString];
NSDictionary *scriptErrorDict = nil;
[script executeAndReturnError:&scriptErrorDict];
if (scriptErrorDict) {
NSLog(@"errorDict: %@", scriptErrorDict);
}
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.
Using the AppleScript
environment, the color mapping is as follows:
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.