How do I tag multiple files from a list in a txt file?

Hi everyone,


I would like to tag over 250 Finder items (i.e. label them with the color Orange) whose file names I have in a txt file.


The txt file list looks like this:


1_A69_2015-11-28_1454_C0046.mov

1_A69_2015-11-28_1410_C0045.mov

1_A69_2015-11-28_1408_C0044.mov

1_A69_2015-11-28_1407_C0043.mov

and so on...

I would like to select all of these files that I know exist on a external drive and tag them with a color.

I have tried to do a boolean search in Finder by typing "1_A69_2015-11-28_1454_C0046.mov OR 1_A69_2015-11-28_1410_C0045.mov OR..." and so forth but Finder doesn't seem to return an accurate list (or it sometimes doesn't return one at all – my list seems to long.)


I understand that Automator is too primitive for this but don't know enough Applescript, Shell script or Terminal commands to make this happen.

Please help!

MacBook Pro with Retina display, OS X El Capitan (10.11.3)

Posted on Mar 1, 2016 1:24 PM

Reply
24 replies

Mar 2, 2016 12:15 PM in response to Myslnik

This post will be my last one in this thread. The following script can add an orange tag to every files whose names are listed in a text file located on the desktop. However, it works well only when the number of files to search through in not too large (it most likely won't work for you). I can't do better. I hope Viking's script will do the job.


set theTextFile to POSIX file "/Users/username/Desktop/myTextFile.txt" as alias-- if the text file is on the desktop


set theFolder to choose folder-- any folder (the smallest possible one) containing all the ".mov" files to tag


set theFilesNames to {}

set theText to readtheTextFile

repeat with thisParagraph in paragraphs of theText

set thisParagraph to contents of thisParagraph

if thisParagraph ends with ".mov" then

copy thisParagraph to the end of theFilesNames

end if

end repeat

--> {1_A69_2015-11-28_1454_C0046.mov, 1_A69_2015-11-28_1410_C0045.mov, … }


tell application "Finder"

with timeout of 3600 seconds

set allTheMovFiles to files of the entire contents of theFolder whose name extension is "mov"

end timeout

repeat with thisFile in allTheMovFiles

set thisFile to thisFile as alias

if name of thisFile is in theFilesNames then

set label index of thisFile to 1

end if

end repeat

end tell

Mar 2, 2016 10:13 AM in response to Myslnik

Here is the recursive version of the previous tags.py, which I am now calling rtags.py (r for recursive). From a user perspective, you will be prompted just as before — for the file containing a list of files to tag, and a second prompt for the starting point for the operation. When it is done, it will just print a line in the terminal indicating how many files were processed. I have added some more comments.


If you want to undo the tag setting, revise the color to none, and run as you did before. If you happen to have Sublime Text 3 editor, open and save a blank Python file, and then from the Edit menu : Paste and Indent. Otherwise, as you did before, and again, try it on a test directory first.


Code:


#!/usr/bin/python

# coding: utf-8

'''

program: rtags.py

Summary: Read master file containing files to be tagged in selected

folder. Utilizes AppleScript for user file and folder access.

Recursively descends into specified folder starting point,

finds file types that match original list and tags them.

Tested: Default OS X 10.11.3 Python v2.7.10.

Version: 1.2

Author: VikingOSX, Mar 2, 2016, Apple Support Communities

'''


from Foundation import NSAppleScript, NSURL, NSURLLabelNumberKey

import os

import sys


colors = {'none': 0, 'grey': 1, 'green': 2, 'purple': 3, 'blue': 4,

'yellow': 5, 'red': 6, 'orange': 7}


# set this to 'none' to recursively remove all tag colors just set

tag_color = 'orange'.lower()

file_type = '.mov'

mov_paths = []

tag_paths = []



AS = '''

property defloc : path to desktop folder

property msg1 : "Provide the input file with .mov filenames:"

property msg2 : "Select folder containing .mov files to tag:"

tell application "Finder"

activate

set movFile to POSIX path of (choose file with prompt msg1 default location defloc invisibles no)

set movFolder to POSIX path of (choose folder with prompt msg2 default location defloc invisibles no)

end tell

return {movFile, movFolder}

'''



def set_tag(afile, acolor):


fileURL = NSURL.fileURLWithPath_(afile)

fileURL.setResourceValue_forKey_error_(colors[acolor],

NSURLLabelNumberKey, None)

return


try:

t = NSAppleScript.alloc().initWithSource_(AS)

result = t.executeAndReturnError_(None)

mov_file = result[0].descriptorAtIndex_(1).stringValue().encode('utf-8')

tag_folder = (result[0].descriptorAtIndex_(2).stringValue().

encode('utf-8').rstrip('/'))

except (AttributeError, TypeError):

sys.exit('User pressed cancel… ending application')


# read in the file with list of .mov to tag.

with open(mov_file, 'r') as f:

mov_list = [x.strip() for x in f.readlines()]


# recursively search inside tag folder for .mov files that match file list

for root, dirs, files in os.walk(tag_folder):

for file in files:

full_path = os.path.join(root, file)

# capture only valid extension file_type

if os.path.splitext(full_path)[1].lower() == file_type:

mov_paths.append(full_path)


# retain only the folder hierarchy where files match original tag list

tag_files = [f for f in mov_paths if os.path.basename(f) in mov_list]

# put specified tag color on .mov files

[set_tag(f, tag_color) for f in tag_files if tag_files]

print("{} '{}' files tagged with {} color\n".format(len(tag_files),

file_type, tag_color))

sys.exit()

Mar 2, 2016 10:56 AM in response to VikingOSX

VikingOSX,


Still doesn't work. I get


0 '.mov' files tagged with orange color


Everytime I run it. And I can see that the matching files are in there. I had both the txt file and a smaller batch of mov files in a folder containing two subfolders on the desktop. I even tried to select one subfolder with no success.


What am I doing wrong?

Mar 2, 2016 12:15 PM in response to Myslnik

Hello


Here's my attempt. The first script is an AppleScript script which is mere wrapper of pyobjc script. To use it, run it in Script Editor and it will let you choose directory where target directory tree is rooted at, the text file containing file names and the label colour to set and then scan the specified directory tree and set label of those files matching the specified names to the specified label index.


The text file containing names are assumed to be in UTF-8. The found files of which label are set are to be returned in the result pane/window of the Script Editor.



--APPLESCRIPT set d to (choose folder with prompt "Choose root directory of target directory tree")'s POSIX path set f to (choose file of type {"txt"} with prompt "Choose text file of name list")'s POSIX path set l to choose from list {"0 none", "1 gray", "2 green", "3 purple", "4 blue", "5 yellow", "6 red", "7 orange"} if l = false then error number -128 -- user cancel set l to l's item 1's character 1 as integer set args to "" repeat with a in {l, d, f} set args to args & ("" & a)'s quoted form & space end repeat do shell script "/usr/bin/python <<'EOF' - " & args & " # coding: utf-8 # # file: # set_label_by_names.py # # usage: # set_label_by_names.py label directory names.txt [names.txt ...] # argv # [1] label : label index in [0..7] for [none, gray, green, purple, blue, yellow, red, orange] # [2] directory : root of target directory tree # [3:] names.txt ... : UTF-8 text file containing target file name per line # # version: # 0.10 # # written by Hiroto, 2016-03 # import sys, os import re, codecs, unicodedata import subprocess import objc from Foundation import * # NSURL, NSNumber # # manual loading of the following bridgesupport metadata is required for pyobjc 2.2b3 under OS X 10.6.8 # FOUNDATION_BRIDGESUPPORT = '''<?xml version=\"1.0\" standalone=\"yes\"?> <!DOCTYPE signatures SYSTEM \"file://localhost/System/Library/DTDs/BridgeSupport.dtd\"> <signatures version=\"0.9\"> <constant name='NSURLLabelNumberKey' type='@'/> <class name='NSURL'> <method selector='resourceValuesForKeys:error:'> <arg type='@' index='0'/> <arg type='^@' index='1' type_modifier='o'/> <retval type='@'/> </method> <method selector='setResourceValue:forKey:error:'> <arg type='@' index='0'/> <arg type='@' index='1'/> <arg type='^@' index='2' type_modifier='o'/> <retval type='B'/> </method> </class> </signatures>''' objc.parseBridgeSupport( FOUNDATION_BRIDGESUPPORT, globals(), objc.pathForFramework('/System/Library/Frameworks/Foundation.framework') ) def set_label(f, lb, **opts): # string f : POSIX path of file or directory # integer lb : label index # label index => colour # 0 => none # 1 => gray # 2 => green # 3 => purple # 4 => blue # 5 => yellow # 6 => red # 7 => orange # dict opts : {'quiet' : boolean} # quiet : True to suppress error message, False otherwise; default = False # return boolean : True if successful, False otherwise. opts.setdefault('quiet', False) b, err = NSURL.fileURLWithPath_(f).setResourceValue_forKey_error_( NSNumber.numberWithInt_(lb), NSURLLabelNumberKey, None) if not b: if not opts['quite']: sys.stderr.write('%s: failed to set label index to %d - %s\\n' % (f, lb, err.description().encode('utf-8'))) return b def main(): argv = [ a.decode('utf-8') for a in sys.argv[1:] ] lbl = int(argv[0]) dir = argv[1] lst = '' for a in argv[2:]: f = codecs.open(a, 'r', 'utf-8') lst += f.read() + '\\n' f.close() nn = [ unicodedata.normalize('NFD', n) for n in re.split(r'\\r\\n|\\r|\\n', lst) if n != '' ] while len(nn) > 0: mm = nn[0:BATCH_SIZE] del nn[0:BATCH_SIZE] pat = '^(.*/)?(%s)$' % '|'.join([ re.escape(m) for m in mm ]) p = subprocess.Popen( ['find', '-E', dir, '-type', 'f', '-iregex', pat, '-print0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = [ a.decode('utf-8') for a in p.communicate() ] ff = [ a for a in out.split('\\0') if a != '' ] for f in ff: print f.encode('utf-8') set_label(f, lbl) BATCH_SIZE = 100 # number of names combined in -iregex primary of find(1) main() EOF" --END OF APPLESCRIPT




And here's the original pyobjc script if you prefer using it in shell.



#!/usr/bin/python # coding: utf-8 # # file: # set_label_by_names.py # # usage: # set_label_by_names.py label directory names.txt [names.txt ...] # argv # [1] label : label index in [0..7] for [none, gray, green, purple, blue, yellow, red, orange] # [2] directory : root of target directory tree # [3:] names.txt ... : UTF-8 text file containing target file name per line # # version: # 0.10 # # written by Hiroto, 2016-03 # import sys, os import re, codecs, unicodedata import subprocess import objc from Foundation import * # # manual loading of the following bridgesupport metadata is required for pyobjc 2.2b3 under OS X 10.6.8 # FOUNDATION_BRIDGESUPPORT = '''<?xml version="1.0" standalone="yes"?> <!DOCTYPE signatures SYSTEM "file://localhost/System/Library/DTDs/BridgeSupport.dtd"> <signatures version="0.9"> <constant name='NSURLLabelNumberKey' type='@'/> <class name='NSURL'> <method selector='resourceValuesForKeys:error:'> <arg type='@' index='0'/> <arg type='^@' index='1' type_modifier='o'/> <retval type='@'/> </method> <method selector='setResourceValue:forKey:error:'> <arg type='@' index='0'/> <arg type='@' index='1'/> <arg type='^@' index='2' type_modifier='o'/> <retval type='B'/> </method> </class> </signatures>''' objc.parseBridgeSupport( FOUNDATION_BRIDGESUPPORT, globals(), objc.pathForFramework('/System/Library/Frameworks/Foundation.framework') ) def set_label(f, lb, **opts): # string f : POSIX path of file or directory # integer lb : label index # label index => colour # 0 => none # 1 => gray # 2 => green # 3 => purple # 4 => blue # 5 => yellow # 6 => red # 7 => orange # dict opts : {'quiet' : boolean} # quiet : True to suppress error message, False otherwise; default = False # return boolean : True if successful, False otherwise. opts.setdefault('quiet', False) b, err = NSURL.fileURLWithPath_(f).setResourceValue_forKey_error_( NSNumber.numberWithInt_(lb), NSURLLabelNumberKey, None) if not b: if not opts['quite']: sys.stderr.write('%s: failed to set label index to %d - %s\n' % (f, lb, err.description().encode('utf-8'))) return b def main(): argv = [ a.decode('utf-8') for a in sys.argv[1:] ] lbl = int(argv[0]) dir = argv[1] lst = '' for a in argv[2:]: f = codecs.open(a, 'r', 'utf-8') lst += f.read() + '\n' f.close() nn = [ unicodedata.normalize('NFD', n) for n in re.split(r'\r\n|\r|\n', lst) if n != '' ] while len(nn) > 0: mm = nn[0:BATCH_SIZE] del nn[0:BATCH_SIZE] pat = '^(.*/)?(%s)$' % '|'.join([ re.escape(m) for m in mm ]) p = subprocess.Popen( ['find', '-E', dir, '-type', 'f', '-iregex', pat, '-print0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = [ a.decode('utf-8') for a in p.communicate() ] ff = [ a for a in out.split('\0') if a != '' ] for f in ff: print f.encode('utf-8') set_label(f, lbl) BATCH_SIZE = 100 # number of names combined in -iregex primary of find(1) main()




Scripts are briefly tested with pybojc 2.2b3 under OS X 10.6.8 but no warranties of any kind.


Please make sure you have complete backup of the original directories before running this sort of script although I think at worst it should fail to set labels and not destroy directories or files.


Good luck,

H

Mar 2, 2016 12:17 PM in response to Myslnik

Are you receiving any Python errors in the Terminal?


I ran this script on a nested folder containing multiple '.mov' files and only those in the list file were tagged. The first dialog is the text file containing the list of files to tag. The second dialog is the starting folder where the script will descend looking for files to tag.


I just ran my copy again and it worked as it did before. I copy/pasted v1.2 rtags.py (my last code post) back to a new Python file, and ran it — with the same outcome as my local rtags.py script. So although the hosting software does take liberties with the formatting, the script that I posted works in my test environment.

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

How do I tag multiple files from a list in a txt file?

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.