Backup Script Advice?

Hi:

I’m working on a script to copy files and folders off of our temporary “Transfer” server to a secondary server and delete the original files. The script is suppose to allow you to select a folder or volume to backup; a target volume where the backup should go; and how aged the files should be that you want to backup.

It basically goes down two levels in the folder hierarchy – it makes a list of the first folder, moves and deletes any files older than the inputted value; moves and deletes any folders old enough; and then looks at any folder with a later modification date and does the same to those sub folders before exiting.

The script works fine when I run it from my local machine on a folder on my local when I select either an external drive or a mounted volume to place the backup. However, the script doesn’t work when I run it from my local on a folder sitting on a mounted volume to that same volume. I wind up with a bunch of empty folders in the backup folder and the files it should have moved have been deleted (not quite what I want it to do).

Anyway, here’s my code – I apologize for the spaghetti nature. I’m still kind of feeling my way with Applescript and most of my stuff has been simple scripts for InDesign workflow. Any suggestions would be appreciated.


display dialog "Archive files Older than how many Days?" default answer "7"
set n to text returned of the result

--select folder to backup
set theFolder to alias choose folder with prompt "Select Folder to Backup"

--select folder where backup is to be placed
set destPath to alias choose folder with prompt "Select Volume to Receive Backup"

display dialog "You are about to archive files from " & theFolder & " older than " & n & " days to " & destPath & "."


--gets list of folder items
set fileList to list folder of theFolder without invisibles

--gets number of items in list
set listNum to number of items of fileList

--coerces folder path to string
set theFolder to theFolder as string
set destPath to destPath as string

--chops up folder path so it can be used later in script
set theChangedPath to items 1 through -2 of theFolder as string
set AppleScript's text item delimiters to ":"
set theName to text item -1 of theChangedPath as string
set theShortPath to text items 1 thru -2 of theChangedPath as string
set AppleScript's text item delimiters to ""

--saves current date in text form in variable backupDate
tell (current date) to tell 100000000 + day * 1000000 + (its month) * 10000 + year as string ¬
	to set backupDate to text 4 thru 5 & "/" & text 2 thru 3 & "/" & text -4 thru -1

--create full name of backup folder
set finalName to theName & " backup " & backupDate

tell application "Finder"
	try
		make new folder at folder destPath with properties {name:finalName}
	on error
		display dialog "Apparently, volume " & destPath & " has not been mounted or a backup folder with this date already exists. Please correct and try again."
	end try
end tell

set subFolderPath to destPath & finalName

repeat with i from 1 to listNum
	with timeout of 100000 seconds
		set tempItem to theChangedPath & ":" & item i of fileList as alias
		tell application "Finder"
			set tempItemType to class of item tempItem
			if (tempItemType is document file) and the modification date of tempItem is less than ((current date) - (n * days)) then
				move tempItem to folder subFolderPath
				delete tempItem
			else if (tempItemType is folder) and the modification date of tempItem is less than ((current date) - (n * days)) then
				move tempItem to folder subFolderPath
				delete tempItem
			else if (tempItemType is folder) and the modification date of tempItem is greater than ((current date) - (n * days)) then
				set tempFileList to list folder of tempItem without invisibles
				set tempListNum to number of items of tempFileList
				
				set tempName to name of folder tempItem
				make new folder at folder subFolderPath with properties {name:tempName}
				
				set tempItem to tempItem as string
				repeat with i from 1 to tempListNum
					set subTempItem to tempItem & item i of tempFileList as alias
					if the modification date of subTempItem is less than ((current date) - (n * days)) then
						move subTempItem to folder (subFolderPath & ":" & tempName)
						delete subTempItem
					end if
				end repeat
			end if
		end tell
	end timeout
end repeat

display dialog "Your backup is complete."


Hi,

Don’t do these kind of “backups”!!!

What you’re trying to do is no backup but some archiving.
Without any verification it could happen that the files are not copied to the destination,
but deleted at the source. The result is losing all data !!!

Stefan:

You’re right – it’s definitely more of an archive than a backup. Searching thru the site under archive I came up with very little. But I think I need to kind of plow ahead in some fashion. We do have tape backups of the server in question – why the script is needed is because the drive just keeps getting filled up and none of the art directors will dump their stuff. So the idea was to try to shove a much of the dated work to another temp server – if someone calls up frantic we could then dump it back on fairly quickly (to access the tape backups is a 24-hour delay). If the script burps, however, it wouldn’t be a complete disaster since we could restore from tape.

That said, I’d like to bulletproof it as much as I can. I think I’m running into issues regarding where the script is running and where the files and archive reside.

If I use a move command on a folder that’s being archived to the same volume, that works fine. However, it doesn’t work when the original work is on one volume and it’s being archived to another.

Would I be better off creating a list of the files to archive; copying them (rather than trying to move them); run some kind of verification test to see that they made it to the archive and then run the list to do the deletes off of the original folder?

Ever see anything like that on the forum?

Thanks.

–jon

Hi jhodges58

take a look at Super Duper, and it’s apple scriptable :smiley:

http://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html

The real solution to your problem is to get the owners of the data to manage it properly:

  • If the data isn’t needed any more it should be deleted.
  • If the data is needed but not in use it should be archived (a couple of copies to be safe).
  • If the data is need and in use and you are running out of disk space, get more storage.

I used to be a storage analyst at some big companies, so I’ve been there, seen it and done it.

I recently wrote a backup script that allows merges jobs based on job numbers. We have jobs on our “Main” server and then we have postscript files that relate to those jobs on a “Secondary” server. The script has the user enter a job number, then the files are copied from the Secondary server to the Main server. At that point, the script verifies the byte count of both folders, if it matches, then it deletes the source files. Then it compresses the folder and if the compression was successful, it deletes the uncompressed files. It has been working flawlessly for about 6 months.

Anyway, that’s a suggestion to address the accidental deletion of files issue.

When I run your script on my local machine, it empties the source folder, and doesn’t move anything. Is this what’s happens when you run this on your servers?

I don’t completely understand your requirements. Are you trying to backup folders with a stale modification date, or are you trying to move files with a stale mod date? I’ve seen situations on our servers where the folder mod date doesn’t update correctly. Do you not care about retaining your folder structure? How deep into a folder structure do you want to go?

I’ll analyze your script a little farther and see if I can figure out where things are going wrong. I’ll let you know if I find anything.

Brian

Ok, I guess I answered my own question by actually READING!! :rolleyes:

I found if I replaced your “move” command with “duplicate”, everything seems to work the way I think you want them. I don’t know how your folders are setup, the way your script is right now, if the source and destination are on the same drive, the finder will actually move the folder, then the delete command deletes it. Therefore, you end up with empty folders.

If you perform a “duplicate”, it copies the source, then deletes the source. So even if the two are on the same drive, it will work.

Let me know if that helps, or if you need help verifying the copy once it’s completed.

The only thing that I found that I didn’t like (other than the lack of copy verification), is that it created a sub-folder even though it didn’t copy anything into it.

Brian

Here’s what I ran:



display dialog "Archive files Older than how many Days?" default answer "7"
set n to text returned of the result

--select folder to backup
set theFolder to alias choose folder with prompt "Select Folder to Backup"

--select folder where backup is to be placed
set destPath to alias choose folder with prompt "Select Volume to Receive Backup"

display dialog "You are about to archive files from " & theFolder & return & "Older than " & n & " days." & return & "To " & destPath & "."


--gets list of folder items
set fileList to list folder of theFolder without invisibles

--gets number of items in list
set listNum to number of items of fileList

--coerces folder path to string
set theFolder to theFolder as string
set destPath to destPath as string

--chops up folder path so it can be used later in script
set theChangedPath to items 1 through -2 of theFolder as string
set AppleScript's text item delimiters to ":"
set theName to text item -1 of theChangedPath as string
set theShortPath to text items 1 thru -2 of theChangedPath as string
set AppleScript's text item delimiters to ""

--saves current date in text form in variable backupDate
tell (current date) to tell 100000000 + day * 1000000 + (its month) * 10000 + year as string ¬
	to set backupDate to text 4 thru 5 & "/" & text 2 thru 3 & "/" & text -4 thru -1

--create full name of backup folder
set finalName to theName & " backup " & backupDate

tell application "Finder"
	try
		make new folder at folder destPath with properties {name:finalName}
	on error
		display dialog "Apparently, volume " & destPath & " has not been mounted or a backup folder with this date already exists. Please correct and try again."
	end try
end tell

set subFolderPath to destPath & finalName

repeat with i from 1 to listNum
	with timeout of 100000 seconds
		set tempItem to theChangedPath & ":" & item i of fileList as alias
		--display dialog theChangedPath
		tell application "Finder"
			set tempItemType to class of item tempItem
			if (tempItemType is document file) and the modification date of tempItem is less than ((current date) - (n * days)) then
				duplicate tempItem to folder subFolderPath
				delete tempItem
			else if (tempItemType is folder) and the modification date of tempItem is less than ((current date) - (n * days)) then
				duplicate tempItem to folder subFolderPath
				delete tempItem
			else if (tempItemType is folder) and the modification date of tempItem is greater than ((current date) - (n * days)) then
				
				set tempFileList to list folder of tempItem without invisibles
				set tempListNum to number of items of tempFileList
				
				set tempName to name of folder tempItem
				make new folder at folder subFolderPath with properties {name:tempName} -- Creates a Blank folder if the contents don't meet criteria
				
				set tempItem to tempItem as string
				repeat with i from 1 to tempListNum
					set subTempItem to tempItem & item i of tempFileList as alias
					if the modification date of subTempItem is less than ((current date) - (n * days)) then
						
						duplicate subTempItem to folder (subFolderPath & ":" & tempName)
						delete subTempItem
					end if
				end repeat
			end if
		end tell
	end timeout
end repeat

display dialog "Your backup is complete."



bcman:

Thanks for your suggestions – I’ll give them a real world test on the servers in question tomorrow. Never used the “duplicate” command before, so I’m anxious to give it a try.

Jonny, of course, is correct – the volume should be self policed. Unfortunately, the creatives at our place are totally undisciplined and no one is willing to slap down these 25-year-old kids making six-figure salaries. The data center contributed to the problem by just adding terabtyes of space until they can no longer run tape backups that can be completed within 48 hours over the weekend. Meanwhile, the size of the files continue to grow so eventually something will have to give. Ugly.

Many thanks for those who took the time to reply.

–jon

bcman:

Tried the revised script out at work, running it from my terminal against a folder on one mounted volume with another volume as the archive target.

First time I run the script it creates an empty archive folder. Second time I run it, it actually does what it’s suppose to do.

I’m just having trouble understanding why the first time it runs there’s a hiccup. I guess I’ll try stepping through it line by line.

-jon

Jon,
I’m not sure why you’re getting that prob. I’ve run the script on our servers 4-5 times, with no probs.

After thinking about it, there are a few mods that I would do on the script, but nothing that would really address the issue that you’re having.

Let me know if I can help anymore.

Brian

Hey Jon,

Don’t know if you want to check this out, but I threw this together because I was looking for a project to try some recursion on, and this seemed like a good one. I setup the script to go into each subfolder and look at the contents of each. That’s where the recursion is.

Hope this helps. Let me know if you have any questions, or if this doesn’t work right. I’m going to use a variation of this myself, so let me know if you find any bugs.

Brian

--Get User inputs
set fileAge to text returned of (display dialog "Archive files older than how many days?" default answer 7)
set theFolder to alias choose folder with prompt "Select folder to backup"
set destFolder to alias choose folder with prompt "Select folder to recieve backup"

--Confirm
display dialog ("You are about to archive files from:" & return & theFolder & return & return & "Older than: " & fileAge & " days" & return & return & "To:" & return & destFolder)

--Set the root folder name
set AppleScript's text item delimiters to ":"
set finalName to (text item ((count of items of theFolder) - 2) of (theFolder as text)) & " Backup " & (do shell script "date '+%m/%d/%y'")

--Call recursive subroutine
checkFolder(theFolder, destFolder, finalName, fileAge)


on checkFolder(subfolder, destination, folderName, age)
	tell application "Finder"
		--Set new folder name
		set newFolder to (destination as text) & folderName
		
		--Check to see if the folder already exists, if not, create one
		if not (folder newFolder exists) then
			try
				make new folder at folder destination with properties {name:folderName}
			on error
				display dialog "Folder could not be created. Please check your permissions, and try again."
			end try
		end if
		
		--Setup file list, and run through list
		set fileList to list folder subfolder without invisibles
		repeat with tempItem in fileList
			set itemPath to (subfolder as text) & tempItem as alias
			
			--Check the mod date of the current item
			if (the modification date of itemPath is less than ((current date) - (age * days))) then
				
				--You may want to modify this to account for existing files, maybe add an extention on to the new copy
				duplicate itemPath to folder (((destination as text) & folderName) as alias) with replacing
				
				--get info for Source and Destination files
				set s_info to info for itemPath
				set d_path to ((destination as text) & folderName & ":" & (text item -1 of (itemPath as text))) as alias
				set d_info to info for d_path
				
				--Compare file sizes, if they don't match you may want to add some error handling
				if size of s_info = size of d_info then
					delete itemPath
				end if
			else
				--This is where the recurssion comes in, if the item is a folder that has been modified recently, this will look into it, and check all files/folders
				if (class of item itemPath is folder) then
					my checkFolder(itemPath, ((destination as text) & folderName) as alias, tempItem, age)
				end if
			end if
		end repeat
		
		--If a folder was created above, and nothing was moved into it, this will remove that folder
		set itemCount to list folder (((destination as text) & folderName) as alias)
		if ((count of items in itemCount) = 0) then
			delete folder ((destination as text) & folderName) as alias
		end if
	end tell
	
	return
end checkFolder

bcmann:

Sorry for the delay getting back to you – I love the recursive portion of the script. Works really well. I added one more chunk of code to kill any empty folders from the originating folders and had to kill the error checking portion (I running this from a Sun box to an XServe and the file sizes never quite match up so it was screwing up too often). I’ll probably add back some kind of error checking later.

However, when I go to make an application of the script below and then try running it, I get the error “every item of alias “Redworks:Test Folder:” doesn’t understand the count message.” (Redworks:Test Folder is the folder being backed up). I’m trying to generate the app from an Intel Mac so I did it as an Application (Bundle) but I’ve tried Carbon as well with no joy.

Runs fine in Script Debugger 4 as well as Apple’s Script Editor, but I can’t seem to make a working app out of it. Tried making the app out of Script Editor as well with the same results.

This is the first time I’ve had a working script that won’t form an app. Any ideas how to troubleshoot something like that?

–jon

--Get User inputs
set fileAge to text returned of (display dialog "Archive files older than how many days?" default answer 7)
set theFolder to choose folder with prompt "Select folder to backup"
--set theFolder to alias choose folder with prompt "Select folder to backup"
set destFolder to alias choose folder with prompt "Select folder to recieve backup"

--Confirm
display dialog ("You are about to archive files from:" & return & theFolder & return & return & "Older than: " & fileAge & " days" & return & return & "To:" & return & destFolder)

--Set the root folder name
set AppleScript's text item delimiters to ":"
set finalName to (text item ((count of items of theFolder) - 2) of (theFolder as text)) & " Backup " & (do shell script "date '+%m/%d/%y'")

--Call recursive subroutine
checkFolder(theFolder, destFolder, finalName, fileAge)



display dialog "Archive is done."


on checkFolder(subfolder, destination, folderName, age)
	tell application "Finder"
		--Set new folder name
		set newFolder to (destination as text) & folderName
		
		--Check to see if the folder already exists, if not, create one
		if not (folder newFolder exists) then
			try
				make new folder at folder destination with properties {name:folderName}
			on error
				display dialog "Folder could not be created. Please check your permissions, and try again."
			end try
		end if
		
		--Setup file list, and run through list
		set fileList to list folder subfolder without invisibles
		repeat with tempItem in fileList
			set itemPath to (subfolder as text) & tempItem as alias
			
			--Check the mod date of the current item
			if (the modification date of itemPath is less than ((current date) - (age * days))) then
				
				--You may want to modify this to account for existing files, maybe add an extention on to the new copy
				duplicate itemPath to folder (((destination as text) & folderName) as alias) with replacing
				delete itemPath
			else
				--This is where the recurssion comes in, if the item is a folder that has been modified recently, this will look into it, and check all files/folders
				if (class of item itemPath is folder) then
					my checkFolder(itemPath, ((destination as text) & folderName) as alias, tempItem, age)
					
					--Removes original folder if now empty
					set itemCount to list folder itemPath without invisibles
					if ((count of items in itemCount) = 0) then
						delete folder (itemPath as alias)
					end if
				end if
			end if
			
			
		end repeat
		
		--If a folder was created above, and nothing was moved into it, this will remove that folder
		set itemCount to list folder (((destination as text) & folderName) as alias)
		if ((count of items in itemCount) = 0) then
			delete folder ((destination as text) & folderName) as alias
		end if
		
	end tell
	
	return
end checkFolder

Jon,
I’m not sure why the application doesn’t like the count command, so I modified the line that sets the finalName variable to:


set finalName to (text item -2 of (theFolder as text)) & " Backup " & (do shell script "date '+%m/%d/%y'")

See if this works for you.
Brian


--Get User inputs
set fileAge to text returned of (display dialog "Archive files older than how many days?" default answer 7)
set theFolder to choose folder with prompt "Select folder to backup"
--set theFolder to alias choose folder with prompt "Select folder to backup"
set destFolder to alias choose folder with prompt "Select folder to recieve backup"

--Confirm
display dialog ("You are about to archive files from:" & return & theFolder & return & return & "Older than: " & fileAge & " days" & return & return & "To:" & return & destFolder)

--Set the root folder name
set AppleScript's text item delimiters to ":"
set finalName to (text item -2 of (theFolder as text)) & " Backup " & (do shell script "date '+%m/%d/%y'")

--Call recursive subroutine
checkFolder(theFolder, destFolder, finalName, fileAge)



display dialog "Archive is done."


on checkFolder(subfolder, destination, folderName, age)
	tell application "Finder"
		--Set new folder name
		set newFolder to (destination as text) & folderName
		
		--Check to see if the folder already exists, if not, create one
		if not (folder newFolder exists) then
			try
				make new folder at folder destination with properties {name:folderName}
			on error
				display dialog "Folder could not be created. Please check your permissions, and try again."
			end try
		end if
		
		--Setup file list, and run through list
		set fileList to list folder subfolder without invisibles
		repeat with tempItem in fileList
			set itemPath to (subfolder as text) & tempItem as alias
			
			--Check the mod date of the current item
			if (the modification date of itemPath is less than ((current date) - (age * days))) then
				
				--You may want to modify this to account for existing files, maybe add an extention on to the new copy
				duplicate itemPath to folder (((destination as text) & folderName) as alias) with replacing
				delete itemPath
			else
				--This is where the recurssion comes in, if the item is a folder that has been modified recently, this will look into it, and check all files/folders
				if (class of item itemPath is folder) then
					my checkFolder(itemPath, ((destination as text) & folderName) as alias, tempItem, age)
					
					--Removes original folder if now empty
					set itemCount to list folder itemPath without invisibles
					if ((count of items in itemCount) = 0) then
						delete folder (itemPath as alias)
					end if
				end if
			end if
			
			
		end repeat
		
		--If a folder was created above, and nothing was moved into it, this will remove that folder
		set itemCount to list folder (((destination as text) & folderName) as alias)
		if ((count of items in itemCount) = 0) then
			delete folder ((destination as text) & folderName) as alias
		end if
		
	end tell
	
	return
end checkFolder


bcmann:

You the man! That worked.

Thanks so much for your time and patience. I’ll run a couple of real world tests tomorrow to see, but at least it compiled into a working app.

–jon