Moving files with AppleScript to specific folder

Hi.

I have several different files in a folder (PDFs and PPTX). I need to move them to specific subfolder, depending on the first 8 characters of the filename.

File names are like this: “ANS01OTHUKE0700.pdf”. Because “UKE0700” in the end can change, the script uses a list like this:

CON00SHE Consultants Overview
CON00PPT Consultants Overview
COR00COV Workbooks
COR01WKB Workbooks
COR02WKB Workbooks
COR03WKB Workbooks
COR04WKB Workbooks
COR05WKB Workbooks
ANS01OTH Answer Sheet
ANS02OTH Answer Sheet

That means "if the filename starts with CON00SHE, put it in folder “Consultants Overview”. But I couldn’t make the script understand that after the first 8 chars, it should be a wildcard. So I reassembled the filename when copying.

Any suggestion?

Thank you,
Luiz

set the_list to (read ("Macintosh HD:Users:xxxxx:DestinationFoldersComplexo.txt" as alias))'s paragraphs
set {TID, text item delimiters} to {text item delimiters, tab}

set languageVersion to "UKE0700.pdf" -- this is the most possible filename end, but it can be .PPTX in some cases, what is not working

tell application "Finder"
	
	
	set theFolder to (choose folder with prompt "Please choose folder to be processed")
	
	
	tell application "Finder"
		set newFolder to (make new folder at theFolder with properties {name:"Files to Upload to Core Units Critical Errors"})
		set thefolderDest to newFolder
		
		
		make new folder at newFolder with properties {name:"Additional materials"}
		make new folder at newFolder with properties {name:"Consultants"}
		make new folder at newFolder with properties {name:"Consultants Overview"}
		
		make new folder at newFolder with properties {name:"Workbooks"}
		make new folder at newFolder with properties {name:"Answer Sheet"}
		
		
	end tell
	
	
	
	repeat with this_para in the_list
		set {source_file, new_name} to text items of this_para
		
		set theFile to (POSIX path of ((theFolder as text) & source_file & languageVersion))
		
		
		set theFileDest to (POSIX path of ((((thefolderDest) as text) & new_name & ":")) & source_file & languageVersion)
		

		
		
		try
			do shell script "cp " & quoted form of theFile & " " & quoted form of theFileDest
		
			
		end try
		
	end repeat
	
	set AppleScript's text item delimiters to TID
	
end tell



This isn’t robust or optimal, but something like…

set sourceFolder to alias "Macintosh HD:Users:paulskinner:Desktop:to sort:"
set sortedFiles to alias "Macintosh HD:Users:paulskinner:Desktop:sortedFiles:"

set sortTable to {{"ANS01OTH", "Answer Sheet"}, {"CON00SHE", "Consultants Overview"}, {"CON00PPT", "Consultants Overview"}, {"COR00COV", "Workbooks"}, {"COR01WKB", "Workbooks"}, {"COR02WKB", "Workbooks"}, {"COR03WKB", "Workbooks"}, {"COR04WKB", "Workbooks"}, {"COR05WKB", "Workbooks"}, {"ANS02OTH", "Answer Sheet"}}

tell application "Finder"
   set fileList to (every file of sourceFolder) as alias list
   repeat with thisSortEntry in sortTable
   	set nameRoot to item 1 of thisSortEntry
   	set targetFolderName to item 2 of thisSortEntry
   	repeat with thisFile in fileList
   		set thisFileName to name of thisFile
   		if text 1 thru 8 of thisFileName is nameRoot then
   			try
   				set thisTargetFolder to ((sortedFiles as text) & targetFolderName) as alias
   			on error
   				set thisTargetFolder to make new folder at sortedFiles with properties {name:targetFolderName}
   			end try
   			move thisFile to thisTargetFolder
   		end if
   	end repeat
   end repeat
end tell

You could change this line to

if thisFileName starts with nameRoot then

Here is the script modified to use only one repeat loop…

set sourceFolder to (path to desktop folder as text) & "to sort:"
set sortedFiles to (path to desktop folder as text) & "sortedFiles:"
try
	sortedFiles as alias
on error
	tell application "Finder" to make new folder at (path to desktop folder as alias) with properties {name:"sortedFiles"}
end try
set sortTable to {"Answer Sheet", "Consultants Overview", "Consultants Overview", "Workbooks", "Workbooks", "Workbooks", "Workbooks", "Workbooks", "Workbooks", "Answer Sheet"}
set rootTable to "ANS01OTH,CON00SHE,CON00PPT,COR00COV,COR01WKB,COR02WKB,COR03WKB,COR04WKB,COR05WKB,ANS02OTH"

tell application "Finder"
	set fileList to (every file of folder sourceFolder) as alias list
	repeat with thisFile in fileList
		set thisFileName to name of thisFile
		set off to offset of (text 1 thru 8 of thisFileName) in rootTable
		if off > 0 then
			set off to 1 + off div 9
			set targetFolderName to item off of sortTable
			try
				set thisTargetFolder to (sortedFiles & targetFolderName) as alias
			on error
				set thisTargetFolder to make new folder at (sortedFiles as alias) with properties {name:targetFolderName}
			end try
			move thisFile to thisTargetFolder
		end if
	end repeat
end tell
1 Like

Neater and possibly a bit more efficient with a whose clause. I did no timing tests.

set sourceFolder to (path to desktop folder as text) & "to sort:"
set sortedFolder to (path to desktop folder as text) & "sortedFiles:"
set sortTable to {{"ANS01OTH", "Answer Sheet"}, {"CON00SHE", "Consultants Overview"}, {"CON00PPT", "Consultants Overview"}, {"COR00COV", "Workbooks"}, {"COR01WKB", "Workbooks"}, {"COR02WKB", "Workbooks"}, {"COR03WKB", "Workbooks"}, {"COR04WKB", "Workbooks"}, {"COR05WKB", "Workbooks"}, {"ANS02OTH", "Answer Sheet"}}

tell application "Finder"
	repeat with sortData in sortTable
		set fileList to (every file of folder sourceFolder whose name begins with (item 1 of sortData)) as alias list
		try
			set thisTargetFolder to ((sortedFolder as text) & item 2 of sortData) as alias
		on error
			set thisTargetFolder to make new folder at sortedFolder with properties {name:item 2 of sortData}
		end try
		move fileList to thisTargetFolder
	end repeat
end tell

I don’t know the full contents of the sourceFolder, but I believe that the sortTable could be reduced to three entries.

set sortTable to {{"ANS", "Answer Sheet"}, {"CON", "Consultants Overview"}, {"COR", "Workbooks"}}
2 Likes

Here is a solution using AppleScriptObjC.
It’s more reliable and should be faster.
If a file already exists in its destination folder, the oldest will be moved to the trash.

use framework "Foundation"
use scripting additions

set theRec to {CON00SHE:"Consultants Overview", CON00PPT:"Consultants Overview", COR00COV:"Workbooks", COR01WKB:"Workbooks", COR02WKB:"Workbooks", COR03WKB:"Workbooks", COR04WKB:"Workbooks", COR05WKB:"Workbooks", ANS01OTH:"Answer Sheet", ANS02OTH:"Answer Sheet"}
set theDict to (current application's NSArray's arrayWithObject:theRec)'s firstObject()
set theFileManager to current application's NSFileManager's |defaultManager|()

set theFolderURL to current application's NSURL's fileURLWithPath:"/Some/folder"
set theFileManager to current application's NSFileManager's defaultManager()
set theArray to theFileManager's contentsOfDirectoryAtURL:theFolderURL includingPropertiesForKeys:{} options:6 |error|:(missing value)
set theFiles to (theArray's filteredArrayUsingPredicate:(current application's NSPredicate's predicateWithFormat:"pathExtension IN [cd] %@" argumentArray:{{"pdf", "pptx"}}))
if theFiles's |count|() = 0 then return beep

repeat with aFile in theFiles
	set aParent to aFile's URLByDeletingLastPathComponent()
	set aName to aFile's lastPathComponent()
	set aSub to (aName's substringToIndex:8)
	set aFold to (aParent's URLByAppendingPathComponent:(theDict's valueForKey:aSub))
	set {theResult, theError} to (theFileManager's createDirectoryAtURL:aFold withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
	if not (theResult as boolean) then error (theError's localizedDescription() as string)
	set aDest to (aFold's URLByAppendingPathComponent:aName)
	if (aDest's checkResourceIsReachableAndReturnError:(missing value)) then (theFileManager's trashItemAtURL:aDest resultingItemURL:(reference) |error|:(missing value))
	set {theResult, theError} to (theFileManager's moveItemAtURL:aFile toURL:aDest |error|:(reference))
	if not (theResult as boolean) then error (theError's localizedDescription() as string)
end repeat
1 Like

The OP’s script gets the file and folder data from a tab-delineated text file. FWIW, I edited Paul’s script do this.

set sourceFolder to (path to desktop as text) & "to sort:"
set sortedFolder to (path to desktop as text) & "sortedFiles:"
set dataFile to (path to desktop as text) & "Data File.txt"

try
	set sortTable to getFileFolderData(dataFile)
on error
	display dialog "An error occurred while reading the data file" buttons {"OK"} cancel button 1 default button 1
end try

tell application "Finder"
	repeat with sortData in sortTable
		set fileList to (every file of folder sourceFolder whose name begins with (item 1 of sortData)) as alias list
		try
			set thisTargetFolder to ((sortedFolder as text) & item 2 of sortData) as alias
		on error
			set thisTargetFolder to make new folder at sortedFolder with properties {name:item 2 of sortData}
		end try
		move fileList to thisTargetFolder
	end repeat
end tell

on getFileFolderData(theFile)
	set {TID, text item delimiters} to {text item delimiters, tab}
	set theData to paragraphs of (read file theFile)
	set theList to {}
	repeat with anItem in theData
		if anItem > "" then set end of theList to {text item 1 of anItem, text item 2 of anItem}
	end repeat
	set text item delimiters to TID
	return theList
end getFileFolderData
1 Like

Good point peavine. I suspect they receive a new file regularly with the data files. Here’s my take on parsing the read data.

set sourceFolder to (path to desktop as text) & "to sort:"
set sortedFolder to (path to desktop as text) & "sortedFiles:"
set sortTable to readSortData((path to desktop as text) & "Data File.txt")

tell application "Finder"
	repeat with sortData in sortTable
		set fileList to (every file of folder sourceFolder whose name begins with (item 1 of sortData)) as alias list
		try
			set thisTargetFolder to ((sortedFolder as text) & item 2 of sortData) as alias
		on error
			set thisTargetFolder to make new folder at sortedFolder with properties {name:item 2 of sortData}
		end try
		move fileList to thisTargetFolder
	end repeat
end tell

on readSortData(theFile)
	set theData to read ((theFile) as alias) as list using delimiter {linefeed, tab}
	repeat with i from 1 to length of theData by 2
		set item ((i div 2) + 1) of theData to {item i of theData, item (i + 1) of theData}
	end repeat
	return items 1 thru ((i div 2) + 1) of theData
end readSortData

Thank you! I just changed from “move” to “duplicate” and it worked exactly as I needed.

You all helped me a lot!

Let’s assume for a second that you have one main folder which contains all of the files you’re talking about and within that main folder, you also have the three sub folders that you want the various files moved into (depending on the first eight characters of the file names).

This following AppleScript code allows you to choose the parent folder containing all of the files and sub folders. It will then move all of the files to the appropriate sub folders.

activate
set folderPath to quoted form of POSIX path of (choose folder)

set moveFiles to "cd " & folderPath & " sleep 1 ;mv {CON00SHE*,CON00PPT*} \"Consultants Overview/\" ;mv {ANS01OTH*,ANS02OTH*} \"Answer Sheet/\" ;mv {COR00COV*,COR01WKB*,COR02WKB*,COR03WKB*,COR04WKB*,COR05WKB*} Workbooks/"

do shell script moveFiles

If you want to trim the fat a little bit and shorten the code, this works also…

activate
set folderPath to quoted form of POSIX path of (choose folder)

do shell script "cd " & folderPath & " sleep .5 ;mv {CON00SHE*,CON00PPT*} 'Consultants Overview' ;mv ANS0*OTH* 'Answer Sheet' ;mv {COR00COV*,COR*WKB*} Workbooks"

I like to try using the least amount of code as possible. In a lot of cases I find it easier to understand exactly what’s going on. Especially if it can avoid using complex delimiters and repeat loops.

Wouldn’t it be more robust to use “.” instead of “*”? Like this: ANS0.OTH*

BTW, how many files does this script need to move each time you use it?

Just asking because if it is many, than it is better to use “System Events” over “Finder” for speed reasons.

About 30 files. Not much.