Search code examples
objective-cxcodexcode6nsdictionarymdls

Convert output of mdls to NSDictionary


Running BSD's metadata tool in Terminal on a CSV file ($ mdls foo.csv) will produce output like this:

kMDItemContentCreationDate     = 2014-08-27 15:28:16 +0000
kMDItemContentModificationDate = 2014-08-27 15:28:16 +0000
kMDItemContentType             = "public.comma-separated-values-text"
kMDItemContentTypeTree         = (
    "public.comma-separated-values-text",
    "public.delimited-values-text",
    "public.text",
    "public.data",
    "public.item",
    "public.content"
)
kMDItemDateAdded               = 2014-08-27 15:28:16 +0000
kMDItemDisplayName             = "foo.csv"
kMDItemFSContentChangeDate     = 2014-08-27 15:28:16 +0000
kMDItemFSCreationDate          = 2014-08-27 15:28:16 +0000
kMDItemFSCreatorCode           = ""
kMDItemFSFinderFlags           = 0
kMDItemFSHasCustomIcon         = (null)
kMDItemFSInvisible             = 0
kMDItemFSIsExtensionHidden     = 0
kMDItemFSIsStationery          = (null)
kMDItemFSLabel                 = 0
kMDItemFSName                  = "foo.csv"
kMDItemFSNodeCount             = (null)
kMDItemFSOwnerGroupID          = 20
kMDItemFSOwnerUserID           = 501
kMDItemFSSize                  = 962
kMDItemFSTypeCode              = ""
kMDItemKind                    = "comma-separated values"
kMDItemLogicalSize             = 962
kMDItemPhysicalSize            = 4096

I would like to capture this output in code and convert it to a NSDictionary.

...

//
// run mdls on file
//

NSURL *url = @"path/to/file/foo.csv";

NSPipe *pipe = [NSPipe pipe];
NSFileHandle *file = pipe.fileHandleForReading;

NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/mdls";
task.arguments = @[url];
task.standardOutput = pipe;

[task launch];

NSData *data = [file readDataToEndOfFile];
NSString *content = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];

//
// populate dictionary
//
NSDictionary *dictionary = [NSDictionary alloc] init];

// pseudo-code
NSArray *rows = split string on \n to create array of rows;

for each row {

  if (row doesn't end with a '(' or start with a ')') { 
    split rows on \= to create key and value;
    add key and value to dictionary;
  }

  else if (row starts with a '(') {
    add key to dictionary; 
    create NSArray; 
    set marker on;
  }

  else if (row ends with a ')') {
    add value to dictionary;
    set marker off;
  }

  if (marker) {
    add value to array;
  }
}

//

Is there a more-elegant approach?


Solution

  • Is there a more-elegant approach?

    Boy, is there ever! ;)

    Why are you trying to parse the output of the mdls command when that command is built on APIs you can use yourself?

    MDItemRef item = MDItemCreateWithURL(NULL, (__bridge CFURLRef)url);
    CFArrayRef names = MDItemCopyAttributeNames(item);
    NSDictionary* dictionary = CFBridgingRelease(MDItemCopyAttributes(item, names));
    CFRelease(names);
    CFRelease(item);
    

    (And, just because you asked about parsing the output of mdls, I'll point out that it has a -plist option. If you use it and specify - as the file, you can read a plist from its stdout and parse it using NSPropertyListSerialization.)