Move Files by Filename script... please help!

Hi there guys & girls,

I am very new to Apple Scripting and I am desperately after a bit of help with writing a reliable Folder Action Script that does just one main job.

Basically, I have a folder on our network called “Processed” where picture files get put after they have been through a conversion system. All these pictures have a two letter code followed by a hyphen at the beginning of their filename. (eg. “li-lifestylepic.jpg” or “pr-39ashmead.jpg”)

Based on this code at the beginning of the filename, the files need to be moved to separate folders on the network. The only other requirement of the script is that if it finds a file in the “Processed” folder that doesn’t have one of these codes in the filename, it will move the file to another folder on the network called “misnamed”

I have researched this on the internet and have come up with the following that I am guessing may work. It will be a few days until I get to try this out unfortunately so really wondered if someone could take a quick look and see whether I am at least going the right sort of direction. My script so far goes as follows:


on open Processed
	
	tell application "Finder"
		
		repeat with indvFile in Processed
			
			set fileName to name of indvFile as text
			try
				
				if fileName contains "li-" then
					move indvFile to folder "OPI:IN:Lifestyle:" with replacing
					
				else if fileName contains "pr-" then
					move indvFile to folder "OPI:IN:Property:" with replacing
					
				else if fileName contains "gg-" then
					move indvFile to folder "OPI:IN:Gazette:" with replacing
					
				else
					move indvFile to folder "OPI:IN:MISNAMED:" with replacing
					
				end if
			end try
			
		end repeat
	end tell
	
end open

I thank people in anticipation of your responses…

What you had was a droplet script - they start with ‘on open’ and act on files dropped on the icon of the script.

What you want is a folder action. They go like this:


on adding folder items to thisFolder after receiving theFiles
	-- where thisFolder (or any other name) is the folder into which a file was added
	-- and theFiles is a list of whatever was dropped in, even if only one thing.
	tell application "Finder" to repeat with indvFile in theFiles
		set fileName to name of indvFile as text
		try
			if fileName contains "li-" then
				move indvFile to folder "OPI:IN:Lifestyle:" with replacing
			else if fileName contains "pr-" then
				move indvFile to folder "OPI:IN:Property:" with replacing
			else if fileName contains "gg-" then
				move indvFile to folder "OPI:IN:Gazette:" with replacing
			else
				move indvFile to folder "OPI:IN:MISNAMED:" with replacing
			end if
		end try
	end repeat
end adding folder items to

FAs are saved as applications (in the save dialog) in a special folder of the user’s Library:Scripts folder called “Folder Actions”. To work properly, Folder Actions have to be enabled. Go to YourDisk:Library:Scripts:Folder Actions: and run Configure Folder Actions there. That will give you a contextual menu for starting and stopping folder actions.

Thank you very much for your quick reply… it really is a help.

If either yourself or somebody else can answer a few questions for me, I think I would understand that little bit more.

  1. You say “theFiles” is a list of whatever was dropped in. Is the term “theFiles” fine for this then? Does it need to be named something to do with the picture files?

  2. Is “indvFile” a command in Applescript to mean “Indiviual Files”?

  3. Would this script that you have modified for me repeat forever until the folder action is stopped? I need the script to be able to “listen” for files that have been added and move them to the correct folder. Files that get put into this folder could be put in seconds, minutes or hours apart from each other but without user input, the script needs to continue to move these files.

  4. While researching this on the internet, I saw some scripts similar to this that seemed to have a line of code that said something like “repeat with i through number of items”. Is this not needed in my script at all? What does this sort of code line do?

If anyone could answer these questions for me, it would be a great help.

Thank you

To answer…

1: Any name will do… you could have used “theSandwiches”

2: Nope, its a variable made up to refer to an item within a list of defined items… you could have said

aSandwich in theSandwiches

the important thing is how you refer to the list in the first place… as in

on adding folder items to thisFolder after receiving theFiles

3: A folder action is always “waiting” depending on the handler used when the condition is met, in this case a item added to the folder, it springs to life and performs the script for all items added… then it goes dormant waiting for its next victim… err item :smiley:

  1. repeat with i from 1 to count of theFiles

is the same as saying

repeat with indvFile in theFiles

The difference being in the latter example you can refer to indvFile by itself… in the former you would have to refer to

(item i of theFiles)

Hope that helps, let us know if anything needs a little better explaining.

Thanks very much for your very informative answers…

Two more questions…

  1. What does the term “try” do at the top of the script? Would the script not work without this?

  2. If my network was very busy and slow at the time the script tried to write/move a file to another folder, what would happen? Occasionaly the network may get busy and I need to make sure the script will continue to move the files as soon as it can do. (ie. the network is no longer too busy)

Thanks

Try is a safeguard to stop a script from exiting upon failure. With the trys current placement it will try to do the move for a particular file and if it cannot for some reason it will go to the next.

As for #2 I can’t think of an instance where it would not copy… TCP/IP is a self regulating protocol and should adapt to allow connections through by throttling other connections back. It may take a while, but it should get there… Of course you could be limiting the number of concurrent connections on the server… In a case like this the script would try to move the file, fail, and move to the next file.

Right guys,

Thanks for everybodys help… I think I need to ask just one more question…

I got a suprise chance to test the script this morning quickly and it did work… sort of…

…the script copied the files instead of moved them… why is this??

How can I get the script to move rather than copy the files?

I saved the script as an app rather than a script… could this be why? Should it be saved as a script instead?

Thanks again

Probably you moved the file to a location on a server. Then ‘move’ command then work like ‘copy’ pf ‘duplicate’ command , the move command work only on the same disk.

So you can script it like this:

try
move theFile to location as alias
delete theFile
end try

Jan is right. The Finder only moves files in an absolute sense if the move is all on the same volume. Moving from volume to volume (as in another machine for example or to another partition on the same disk or to another drive) it adapts the safe policy of copying instead. Rather than deleting out of hand, however, I’d be inclined to check first that the moved files were actually on the server and then delete the originals.

[Perhaps I should add that the reason is simple: on a volume which is space on a disk, the Finder doesn’t actually move the file physically, it just alters the directory structure pointing to the file. On a move across volumnes, the Finder has to actually move the bits to the new volume and that volume’s directory structure has to be changed.]

The reason for Jan’s “try block” is that if the move fails, the delete won’t happen. If that’s safe enough for you, you don’t need any other check.

As an adder: you should also know that an AppleScript delete is not recoverable - it doesn’t go in the trash

That’s why I use it as this :

Because if the move command not work he error’s before the end try statement, and will not delete theFile.
And not like this:

As I noted, Jan, at the bottom of my post -2.

An alternative I’ve seen used is to move the retained version to a new folder “Retained Trash” so that after a quick review, the contents can be trashed sometime later.

I use your method.

I can’t thank people enough for their help with this, I really wasn’t expecting such well written, informative answers to my questions.

Unfortunately I am still struggling with getting this script to work as perfectly as it needs to.

Firstly, I have sorted the whole move/copy issue. The folder where the picture files get dropped (to which the script is attached) is on the same volume as the folders where it is moving the files to, therefore it should have been moving rather than copying all along.

The problem was that I had written the script so that it had the whole path to each folder. I solved this by providing the path to the folders at the top of the script as an alias and then referring to the variable name further down the script.

Let me explain exactly what is happening here.

Basically, a program called Intellitune is picking photo files up from a specific ‘watched’ folder. Intellitune then alters these photos in different ways and then outputs the files when it has finished with them to another folder. The folder to which it outputs is called ‘Intellitune_Processed’. From here, my script needs to move these photos to the correct folder (on the same volume) based on the beginning part of the filename of the files.

My problem is:

It seems that sometimes the script works fully and sometimes not. The script sometimes deals with all the photos properly and they all disappear and go to the correct folders. Other times, the script seems to move most of the files but leaves maybe two or three (out of the twelve or so I am putting in).

At these times, the script doesn’t look like it has errored in any way. There are certainly no error messages on the screen. The script also hasn’t detached itself from the folder. I can prove that the script is still running because If I leave the photos that it seems to have missed out in the folder and add a few more, more often than not the script will deal properly with the new photos and move them to the correct folders.

It made me wonder if the script is tying to move files before the Intellitune program has properly finished writing the file to the folder. Because of this, I tried dropping photos into the folder
manually. Unfortunately the same thing occurs and some files move sometimes, other times all the files will move. When dropping manually, I have tried putting about twelve files in at once and also tried dropping files in one by one.

The script does seem to do better when I drop the files in individually but again will not always move all the files. Please can somebody help me out here!! I have spent a long time today at work trying different things and nothing seems to make the script work perfectly.

I also tried saving the script as a ‘script’ rather than an application and attached that to the folder instead. It still did it’s job of moving files but again failed to move all the files everytime I tried.

The script I have so far is:

property LifestyleIn : "OPI:IN:Lifestyle:" as alias
property PropertyIn : "OPI:IN:Property:" as alias
property GazetteIn : "OPI:IN:Gazette:" as alias
property ErrorIn : "OPI:IN:Misnamed:" as alias


on adding folder items to thisFolder after receiving theFiles
   tell application "Finder" to repeat with indvFile in theFiles
       set fileName to name of indvFile as text
       try
           if fileName contains "li-" then
               move indvFile to LifestyleIn with replacing
           else if fileName contains "pr-" then
               move indvFile to PropertyIn with replacing
           else if fileName contains "gg-" then
               move indvFile to GazetteIn with replacing
           else
               move indvFile to ErrorIn with replacing
           end if
       end try
   end repeat
end adding folder items to

Please can somebody give me an idea as to what could be causing this problem!

Thank you

This is a very common problem for Folder Actions when a large number of files appear at once - they miss some because the action has started before all the new entries are recorded in “theseFiles” or whatever you call it. The normal way around this is to abandon the Folder Action approach and re-cast it as an ‘on idle’ script.

These scripts are saved as stay-open applications, and take this form:


on run
	-- do stuff - this runs when the application is started to set things up.
	-- It is not necessary to have an 'on run' handler. The on idle
	-- handler runs right after this anyway.
end run

on idle
	-- In this section, you do your folder watching and the actions
	-- that go with it.
	return 60 -- do this every 60 seconds (or whatever)
end idle

on quit
	-- do stuff - assumes you want to clean things up or save
	-- preferences or variables before the program quits.
	continue quit
	-- if there's an on quit handler, continue quit
	-- must be the last statement or the script will
	-- not stop - you've grabbed the indication that
	-- it should quit.
end quit

Your script would become:


(* Commented because I can't compile with files I don't have.
remove the comment delimiters and this comment.
property LifestyleIn : "OPI:IN:Lifestyle:" as alias
property PropertyIn : "OPI:IN:Property:" as alias
property GazetteIn : "OPI:IN:Gazette:" as alias
property ErrorIn : "OPI:IN:Misnamed:" as alias
property Target : missing value
*)

on idle
	tell application "Finder"
		set theFiles to files of entire contents of target
		repeat with indvFile in theFiles
			set fileName to name of indvFile as text
			try
				if fileName contains "li-" then
					move indvFile to LifestyleIn with replacing
				else if fileName contains "pr-" then
					move indvFile to PropertyIn with replacing
				else if fileName contains "gg-" then
					move indvFile to GazetteIn with replacing
				else
					move indvFile to ErrorIn with replacing
				end if
			end try
		end repeat
	end tell
	return 60
end idle

Ah, OK, I did not know that. Keep rewriting my Folder Action scripts each time … I thought (and have tested) that when the scripts wait untill the action on the ‘first’ folder action script is ended and than start the ‘second’ folder action script. ((I mean: a file is put in the folder, the ‘first’ time the folder action scripts runs, and while running a second file is dropped, then comes in the ‘second’ folder action script that waites till the ‘first’ is ready).

Problem with my script was I used the first characters of the file (a PDF) for the action what need to be done with the PDF.
So without the first characters it will move to the server, if had characters (like ‘KS_’) it first would copy to another folder, then rename it, then copy it to a folder with a folderaction script to print te file and copy the same file to the server. And eeeeh… I always reed the manuals after starting, so in the beginning I calculatted a new alias for the (renamed) file. (Now I know there is no need to do that because the alias is linked to the file, also after you moved and renamed him.
With this correction it seems to work always, but we are still testing now. But it also takes some time to write the PDF, maybe therefore it gives no problems.

Right guys,

I took a look at this last night and it looked very promising. I understand now why it probably wasnt moving all the files all of the time. I have come into work this morning and unfortunately, I have one last problem with this new “on idle” script.

When I run the script first time and every 10 seconds (I have “return 10” at the bottom) I get an error message popup. If I name the target folder “Processed” at the top and then refer to the target as “Processed” I get:

Can’t get every file of class <> of alias Processed

What does this mean? The actual whole script I am using is:

property LifestyleIn : "OPI:IN:Lifestyle:" as alias
property PropertyIn : "OPI:IN:Property:" as alias
property GazetteIn : "OPI:IN:Gazette:" as alias
property ErrorIn : "OPI:IN:Misnamed:" as alias
property Processed : "OPI:IN:Intellitune_Processed" as alias

on idle
   tell application "Finder"
       set theFiles to files of entire contents of processed
       repeat with indvFile in theFiles
           set fileName to name of indvFile as text
           try
               if fileName contains "li-" then
                   move indvFile to LifestyleIn with replacing
               else if fileName contains "pr-" then
                   move indvFile to PropertyIn with replacing
               else if fileName contains "gg-" then
                   move indvFile to GazetteIn with replacing
               else
                   move indvFile to ErrorIn with replacing
               end if
           end try
       end repeat
   end tell
   return 10
end idle

I am saving the script as an application and ticking just the box that says “stay open”. Is this correct?

Please somebody help… I feel so close to getting this working!

I have just worked out that the script does do it’s job properly but it needs some extra coding. I think the reason it must be coming up with that error is that when it finds no files in the folder, it doesent know what to do.

I need the script to be able to deal with the folder when it also contains no files. There won’t always be a file in the folder so the script needs to be able to ‘wait’ until there is. I guess the script would still run every 10 seconds but I need it so that it does nothing if it finds no files.

Is this done with more ‘if’ statements?

Something like:

if contents of Processed is equal to 0 then …??

I am stuck!

You could do if statements or a try should work as well…

(*
property LifestyleIn : "OPI:IN:Lifestyle:" as alias
property PropertyIn : "OPI:IN:Property:" as alias
property GazetteIn : "OPI:IN:Gazette:" as alias
property ErrorIn : "OPI:IN:Misnamed:" as alias
property Processed : "OPI:IN:Intellitune_Processed" as alias
*)

on idle
	try
		tell application "Finder"
			set theFiles to files of entire contents of Processed
			repeat with indvFile in theFiles
				set fileName to name of indvFile as text
				try
					if fileName contains "li-" then
						move indvFile to LifestyleIn with replacing
					else if fileName contains "pr-" then
						move indvFile to PropertyIn with replacing
					else if fileName contains "gg-" then
						move indvFile to GazetteIn with replacing
					else
						move indvFile to ErrorIn with replacing
					end if
				end try
			end repeat
		end tell
	end try
	return 10
end idle

Couple of things.

Note that Processed is the only target without an added colon. I added one. It’s a folder.
I’ve made minor changes to Kevin’s version:
You can comment out the error dialog after you’ve seen it a few times.

(*
property LifestyleIn : "OPI:IN:Lifestyle:" as alias
property PropertyIn : "OPI:IN:Property:" as alias
property GazetteIn : "OPI:IN:Gazette:" as alias
property ErrorIn : "OPI:IN:Misnamed:" as alias
property Processed : "OPI:IN:Intellitune_Processed:" as alias
*)

on idle
	tell application "Finder"
		try
			set theFiles to files of entire contents of Processed
		on error tError -- no files or server down, no response
			display dialog tError giving up after 20
			exit repeat
		end try
		-- if we get here, we've got files
		repeat with indvFile in theFiles
			set fileName to name of indvFile as text
			if fileName contains "li-" then
				move indvFile to LifestyleIn with replacing
			else if fileName contains "pr-" then
				move indvFile to PropertyIn with replacing
			else if fileName contains "gg-" then
				move indvFile to GazetteIn with replacing
			else
				move indvFile to ErrorIn with replacing
			end if
		end repeat
	end tell
	return 30 -- should be longer than the worst case processing time
	-- What's the hurry? Do the files have to be moved immediately?
	-- Suppose the Finder is very busy with something else.
end idle

Hello:

Just getting into applescripting. Might have tried to cram all the books I’ve read in too fast, but have been successful so far on some piddly little things like mass renaming of files and labeling items green for go.

I came across this thread from the fall and realized it was just what i wanted.
We publish a dozen different magazines and generate single page PDFs for each page.
Production employees work on 4-5 different magazines per day.
Each mag is coded with a suffix of 7 digits for its title and pub date.

Was writing all PDFs through the same set of distiller options to keep everything the same. This out folder fed into an Enfocus Pitstop folder to preflight all files into a generic “I am a good PDF” folder. Have employees open and inspect the PDFs they wrote individually. Upon completion of this task wanted to have them dump it into one “in” basket and let the file automatically find its final folder and move the PDF into it without having to go locate the proper folder each time. Upon the move, also wanted to “copy” the file onto another server where all proofing takes place. Figure I could add the copy step at a later date since it will have one generic “print me on the xerox” folder.

Remapped the names of the folder and was trying to test, but run into 2 errors. The answer must be staring me in the face.

First error is “the variable trigger is not defined.”
Second is “exit statement is not in a repeat loop.”

Many thanks in advance.

(*
property QHa0507 : "curtis:adpdfs:QHa0507:finalpagesQHa0507" as alias
property QHb0507 : "curtis:adpdfs:QHb0507:finalpagesQHb0507" as alias
property QHa0607 : "curtis:adpdfs:QHa0607:finalpagesQHa0607" as alias
property badnames : "curtis:adpdfs:badnames:" as alias
property trigger : "curtis:adpdfs:trigger:" as alias
*)

on idle
	tell application "Finder"
		try
			set theFiles to files of entire contents of trigger
		on error tError
			display dialog tError giving up after 10
			exit repeat
		end try
		repeat with indvFile in theFiles
			set fileName to name of indvFile as text
			if fileName contains "_QHa0507" then
				move indvFile to QHa0507 with replacing
			else if fileName contains "_QHb0507" then
				move indvFile to QHb0507 with replacing
			else if fileName contains "_QHa0607" then
				move indvFile to QHa0607 with replacing
			else
				move indvFile to badnames with replacing
			end if
		end repeat
	end tell
	return 30
end idle