How can I edit file metadata in OS X?

2019-03-27 14:41发布

问题:

Does anyone know if it is possible to directly edit file metadata on OS X. Specifically in perl. The parameter I'm specifically trying to change is kMDItemFSLabel (The color of the file). I've had a search around and I can't seem to find a way to do this without using a module such as Mac::Glue or an external application (Finder).

回答1:

The kMDItemFSLabel attribute is a property of the Finder. You need to use a way to communicate with the Finder to change its data. As far as I know, there is no bit you can twiddle with Perl to change the Finder's data without going through the Finder.

There are several ways to do this:

  1. Use CamelBones when the new version comes out. That allows a bridge to Objective C from Perl. Then you will need to use the Apple method with Cocoa system calls. Steep learning curve for Cocoa...

  2. If you have developer tools, use /Developer/Tools/SetFile (if that supports the metadata item)

  3. Use osascript to send the message to the Finder to change the color of the file. You can look at this earlier SO post for hints on doing that.

Most of the Perl related Objective C / Cocoa bridges have died unfortunately. MacPerl has not been updated since 2005.

Almost all the easiest methods require knowing at least minimal amount of Applescript and calling the text of that script though an interpolated type call to osascript.

In its 1 line form, osascript makes Perl look beautiful:

osascript -e 'tell application "Finder"' -e "activate" -e "display dialog \"hello\"" -e 'end tell'

To use osascript from Perl, most use a HERE document. There are examples from I book I have called Applescript - The Definitive Guide and from brian d foy on Controlling iTunes with Perl.

Here is a script in Perl I wrote for setting file color using osascript:

#!/usr/bin/perl
use strict; use warnings;
use File::Spec;
use String::ShellQuote; 

sub osahere  { 
    my $rtr;
    my $scr='osascript -ss -e '."'".join ('',@_)."'";
    open my $fh, '-|', $scr or die "death on osascript $!";
    $rtr=do { local $/; <$fh> };
    close $fh or die "death on osascript $!";
    return $rtr;
}

sub set_file_color {
# -- No color = 0
# -- Orange = 1
# -- Red = 2
# -- Yellow = 3
# -- Blue = 4
# -- Purple = 5
# -- Green = 6
# -- Gray = 7

my $file=shift;
my $color=shift || 0;
$color=0 if $color<0;
$color=7 if $color>7;

$file=File::Spec->rel2abs($file) 
    unless File::Spec->file_name_is_absolute( $file );
$file=shell_quote($file);

return undef unless -e $file;

my $rtr=osahere <<"END_SET_COLOR" ;
tell application "Finder"
    set f to "$file"
    set ItemToLabel to POSIX file f as alias
    set the label index of ItemToLabel to $color
end tell
END_SET_COLOR

return $rtr;
}

set_file_color("2591.txt",2);

If the Finder color is 0, kMDItemFSLabel is 0. If there is any color set, kMDItemFSLabel becomes 8-color. ie, label "orange" is label index 1, kMDItemFSLabel = 7; label "red" is label index 2, kMDItemFSLabel = 6; and so on.



回答2:

There is no built-in function in Perl to operate on Mac filesystem metadata. Either your use a library from CPAN, or write it yourself, probably not as well as the author of the one in CPAN did.



回答3:

It is actually not that complicated to implement. You can use the xattr command as shown in the doc string below... I've wrapped the basic function in a python script which applies named colors to a series of files...

#!/usr/bin/env python

"""
==================================
LABELCOLOR.PY - Colorize Finder labels of files

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

The shell command used is:
  xattr -wx com.apple.FinderInfo \
  0000000000000000000400000000000000000000000000000000000000000000 myfile.txt
where 04 in the middle of the zeroes is the color definition
==================================
"""
import sys
import os
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))