serialize files

A script I have been working on processes files and then moves them to a “done” folder. I have not been able to find an efficient way to deal with duplicate file names. This must be such a common problem that I figure someone must have already solved it.

Once I have processed the files I am moving them to the “done” folder with this command:

	tell application "Finder"
			set the source_file to (move this_item to the DoneFolder) as alias
		end tell

Here is the extra twist, I would like to only change the names of the older items already in the folder, I need to keep the name of the newest item the same, in case it needs to be used again. Ideally older items would just get a .1 .2 .3 etc added to the name.

Any help would be appreciated

Early in your ‘run’, you get the name and modification date of every file in the ‘moveTo’ folder:

tell application "Finder" to set d to {name, modification date} of files of alias "moveToFolder"

Then, as you process new files, you see if the name of a file is contained in item 1 of d. If it is, you shuffle through the name list to find out which one it is. Take its modification date (same value of the index) and put it in short sortable form like this:

to date_format(old_date)
	set {year:y, month:m, day:d} to date old_date
	tell (y * 10000 + m * 100 + d) as string to text 3 thru 4 & text 5 thru 6 & text 7 thru 8
end date_format

date_format("July 29, 2006") --> "060729" -- note that the argument is a date as text, not a date
-- If you want to pass it a date class, then remove 'date' from the first line

Append that to the name of the old one, and it will sort appropriately. You can save time by doing this to all your files; then you don’t have to worry about how to shuffle down a list changing 1 to 2, 2 to 3, etc. as newer ones are added. In my view, this is the best way to keep old versions. The current one has a recent date, all older are dated in an easy to read format and they Finder (and ASCII) sort in date order with the same text name, the newest last. If it is possible that a new one will be added on the same day as an earlier one, the handler above can be modified to include hours or hours and minutes to the date label, eg: 0607091542 as yymmddhhMM (year, month, day, hour, minute): July 9, 06 3:42 PM

See this thread too: Nigel Garvey is the author of the handler above.

Date labeling also makes it easy to get one back from a script. You simply find the files whose name contains the textual part, and take the last of them.

It’s not clear from this, Don, whether you want all the duplicate names moved up by one number, or simply to give the next available number to the current holder of the root name. The latter’s simpler to implement (if you still want to do it this way after reading Adam’s advice):

tell application "Finder"
	set source_name to name of this_item
	set target_path to (DoneFolder as Unicode text) & source_name
		target_path as alias -- This will error if the name's not already taken.
		-- No error? A file with that name already exists in the DoneFolder.
		-- Find a new name for that file.
		set suffix to 1
		repeat -- until error
				target_path & "." & suffix as alias
				set suffix to suffix + 1
			on error
				-- When the coercion finally fails, the name tried isn't in use.
				-- Rename the existing file and exit the repeat.
				set name of file target_path to source_name & "." & suffix
				exit repeat
			end try
		end repeat
	end try
	-- Now move the new file into the folder.
	set source_file to (move this_item to DoneFolder)
end tell

If you want all the existing files with that name (and suffixes) to be renumbered, you could use a reverse repeat, once you’ve found the next unused suffix name, in place of the single renaming line above.

In much the same way that Keith Emerson is the author of Ginastera’s First Piano Concerto. :wink:

Sorry, Nigel; :rolleyes:

It’s one of the few scripts of yours or Kai’s that doesn’t contain the suffix to its name (Kai) or (NG), along with a very few others. I’ve changed the attribution above and added (NG) to the original {DateSortable(NG)} in my files. [and I like KE too]


No problem, of course. It’s not a complete insult to have one of my efforts mistaken for one of kai’s “ and that particular version of it is more in kai’s style than in mine. :slight_smile:

Keith Emerson and Kai Edwards! :slight_smile:

Another way to renumber all similarly named items (so that the oldest version has the highest number) is to use a recursive handler.

The following should preserve any name extensions, by inserting the numbers before the extension:

to |renumber items|(t, b, e, i)
	tell application "Finder" to tell t's item (b & i & e) to if exists then
		set i to i + 1
		my |renumber items|(t, b, e, i)
		set name to b & i & e
	end if
end |renumber items|

to |renumber & move|(f, t)
	tell application "Finder"
		if t's class is not folder then set t to folder t
		set n to f's name
		tell t's item n to if exists then
			set {name extension:{text:e, length:l}} to f
			tell l + 1 to set b to n's text 1 thru -(1 mod it + it) & "_"
			if l > 0 then set e to "." & e
			my |renumber items|(t, b, e, 1)
			set name to b & 1 & e
		end if
		move f to t
	end tell
end |renumber & move|

set the source_file to |renumber & move|(this_item, DoneFolder) as alias

(* if the calling statement is inside an application tell block then use: my |renumber & move|etc... *)

You’ve heard my renderings of Bartók and Copland, then? :wink:

I’ll say! Your “|Fanfare| at the |Common Man|” is one of the all-time greats! :wink:


Thanks for all of the replies, I am still sorting out which approach I am going to take.

I like the date/time stamp approach, but I will modify it for this particular situation, since all of the the files will arrive on the same day, (or occasionally over a two day span). I will probably make the file name something like filename_DDHHMMSS that way they will sort nicely and definitely be unique.

I also noticed that there seems to be a preference for “to” instead of “on” to define a handler, is there any reason for this? I just want to make sure I am like the other cool kids! :slight_smile:

Thanks again.

Hi, Don.

It’s entirely a matter of personal preference. Be yourself. :slight_smile:

to date_format(old_date)
set {year:y, month:m, day:d} to date old_date
tell (y * 10000 + m * 100 + d) as string to text 3 thru 4 & text 5 thru 6 & text 7 thru 8
end date_format

I made my attempt at modifying the above handler to add minutes and seconds, but I got stuck with the time as number of seconds from midnight issue

ideally, what I would like is this:
date_format(“July 29, 2006 5:42:04 PM”) → “29_174204”

dd_hhMMSS (day, underbar, hour, minute seconds)

Here’s a modified form that does it: (an extension of Garvey’s script)

to date_format(Now)
	set c to "_"
	tell Now to tell its day to set tDay to text 2 thru -1 of ((100 + it) as string)
	tell Now to tell ((1000000 + (its hours) * 10000 + (its minutes) * 100 + (its seconds)) as string) to set HMS to text 2 thru -1
	return tDay & c & HMS
end date_format

set today to (current date) -- any date including (current date)
set S to date_format(today) --> 20_194941


on |d_hms date format| for d
	tell 100000000 + 1000000 * (d's day) + 10000 * (d's hours) + 100 * (d's minutes) + (d's seconds) as string to text 2 thru 3 & "_" & text 4 thru end
end |d_hms date format|

|d_hms date format| for date "Saturday, January 7, 2006 12:34:56"
--> "07_123456"

Neat, :smiley:

I just modified one I had around that did YYMMDDhhmmss, and for that, the leading number gets too large to be treated as an integer so I needed two lines to avoid a floating point mess.

Someday, I’ll work on this:

to format_time for aDate into |format| around delim
	set formats to {YYYY, YY, MO, dd, hh, mm, ss}
	-- do clever stuff
end format_time

format_time for (current date) into "YYMOddhhmm" around "."

With a wider screen, perhaps I could avoid the folding of longer lines… :wink:

on |YYMMDDhhmmss format| for d
	tell d to tell (10000 * year + 100 * (its month) + day as string) & 1000000 + 10000 * (its hours) + 100 * (its minutes) + (its seconds) to text 3 thru 8 & text 10 thru -1
end |YYMMDDhhmmss format|

|YYMMDDhhmmss format| for date "Thursday, August 9, 2007 12:34:56"
--> "070809123456"

Ahh, very nice – concatination within the ‘tell’ – didn’t give that a thought. Clever. :wink:

Now my goal of doing the rest becomes one of adjusting what is returned according to the format, and I’ll bet I can do that with some offsets.


on |kai's YYMMDDhhmmss handler, but returning to the raison d'être for NG's original leading-zero method| for d
	tell d to text 3 thru 8 of (10000 * year + 100 * (its month) + day as string) & text 2 thru 7 of (1000000 + 10000 * (its hours) + 100 * (its minutes) + (its seconds) as string)
end |kai's YYMMDDhhmmss handler, but returning to the raison d'être for NG's original leading-zero method|

|kai's YYMMDDhhmmss handler, but returning to the raison d'être for NG's original leading-zero method| for date "Thursday 9 August 2007 12:34:56"
--> "070809123456"


An easy-to-forget (or miss entirely) nuance, Mr. G. :wink: