How can I add OS X “tags” to files programmatically?

前端 未结 9 1478
轻奢々
轻奢々 2021-01-29 22:23

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

\"finder

Is there any

相关标签:
9条回答
  • 2021-01-29 22:29

    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).

    0 讨论(0)
  • 2021-01-29 22:36

    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)
    
    0 讨论(0)
  • 2021-01-29 22:38

    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.

     

    Basics

    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.

     

    Bash script to import tags from another 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
    

    The Automator service

    0 讨论(0)
  • 2021-01-29 22:38

    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.

    0 讨论(0)
  • 2021-01-29 22:45

    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):

    • @(0): None
    • @(1): Grey
    • @(2): Green
    • @(3): Purple
    • @(4): Blue
    • @(5): Yellow
    • @(6): Red
    • @(7): Orange

    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.
    0 讨论(0)
  • 2021-01-29 22:47

    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)
    
    0 讨论(0)
提交回复
热议问题