How to exclude locked files or folders In an AppleScript that selects all files in a particular folder
Here's my situation. I created an AppleScript for cleaning up my desktop. It essentially selects all files and folders on my desktop, creates a new folder with a specific name and date format, and moves all the files from my desktop to the new folder. This script works beautifully until I realized that occasionally there were files or folders I wanted to remain on my desktop. My solution was to open the get info window for each file or folder that I wanted to remain on my desktop, and select the “lock” option in the get info window.
My problem is now because of the locked files, the script cannot completely execute without displaying an error message. If I click “OK” on the error message, the script will finish and move every file with the exception of the ones that are locked.
I would prefer not to have to go through this extra step by clicking the okay button. I tried adding some system event actions to the script to automatically click the “ok” button and that did not work.
I'm starting to think that the only real solution would be to not select those locked files or folders in the first place. This is where I am lost. Can anyone help direct me as to how to avoid selecting the locked files in the first place?
Here is the script in its entirety.
set scriptPath to (load script file "Macintosh HD:Users:Smokestack:Library:Mobile Documents:com~apple~ScriptEditor2:Documents:Cleanup Desktop.scptd:Contents:Resources:Scripts:Current Time A.M. P.M. And Short Date.scpt")
tell scriptPath
timeandDate() -- This will get the time and date in this format "05/31/2017 @ 9:10:48 PM" called from the loaded script file above
end tell
set timeandDate to the result -- This will copy the time and date results from the previous step and and set it as this new variable
tell application "Finder"
if running then
close every window
activate
make new Finder window
set target of Finder window 1 to folder "Desktop" of folder "Smokestack" of folder "Users" of startup disk
end if
open Finder window 1
activate
end tell
delay 1
tell application "System Events"
key code 0 using (command down) -- This will select all files and folders on the desktop in the active finder window
end tell
tell application "Finder"
set these_items to the selection
set destinationFolder to make new folder at POSIX file "/Users/Smokestack/Jimz_Important_Stuff/Desktop_Cleanups/" with properties {name:timeandDate}
move these_items to destinationFolder
reveal destinationFolder
end tell
Here is a revised version of the script without calling handlers from external files
on timeandDate()
set CurrentTime to (time string of (current date))
set AppleScript's text item delimiters to ","
set theLongDate to (current date)
set theLongDate to (date string of theLongDate)
set currentMonth to (word 1 of text item 2 of theLongDate)
set currentDay to (word 2 of text item 2 of theLongDate)
set currentYear to (word 1 of text item 3 of theLongDate)
set monthList to {January, February, March, April, May, June, July, August, September, October, November, December}
repeat with x from 1 to 12
if currentMonth = ((item x of monthList) as string) then
set theRequestNumber to (text -2 thru -1 of ("0" & x))
exit repeat
end if
end repeat
set currentMonth to theRequestNumber
set currentDay to (text -2 thru -1 of ("0" & currentDay))
set theShortDate to (currentMonth & "/" & currentDay & "/" & currentYear) as string
set CurrentTime to (time string of (current date))
set CurrentTimeandShortDate to (theShortDate & " @ " & CurrentTime)
set AppleScript's text item delimiters to ""
end timeandDate
timeandDate()
set timeandDate to the result
tell application "Finder"
close every window
activate
make new Finder window
set target of Finder window 1 to folder "Desktop" of folder "Smokestack" of folder "Users" of startup disk
select every file of the front Finder window
delay 1
set these_items to the selection
set destinationFolder to make new folder at POSIX file "/Users/Smokestack/Jimz_Important_Stuff/Desktop_Cleanups/" with properties {name:timeandDate}
try
move these_items to destinationFolder
end try
reveal destinationFolder
end tell
After making some revisions to the script, as long as the locked items on my desktop are only folders and not files, now this script works beautifully. However it will still generate an error if there are any individual locked files.
Update: As I mentioned somewhere in the comments that I'd build out a 10.12.5 system this past weekend and do some testing, I did.
Here is what I found out:
- The code of my answer, inclusive of edit 5 on Jun 2 at 15:34, works on my system without issue.
- After a review of all code and comments, it dawned on me that while my code, as is, worked on my system and I gave note referencing the
delay
command having to adjust values and or add/remove as/if necessary, etc., that this mainly comes down to timing issues and the pitfalls of that particular form of event coding.
Therefore, and while I'll leave the original answer at the bottom of this update, I decided to look at this from a different programatic angle and offer the following code as a better way to code this script to accomplish the task at hand.
This new code, does not rely on closing opened Finder windows and then opening a Finder window to the Desktop folder of the User to then have System Events select all items in the target folder, or the use of the delay
command to deal with timing issues etc. All of which I personally don't like, because I don't want all Finder windows I have opened, closed to accomplish a task such as this. Or code it to leave the existing windows opened while ensuring the Finder window of the Desktop folder within the Home folder is the topmost Finder window to ensure System Events selects the correct items and get the timings right, etc.!
The following AppleScript code offers a more safe and faster way to accomplish the task at hand and I encourage you to use it over the previous code examples.
Note: With the previous caveats in play, (the existence of the folder the destination folder is created in, etc.), you should not not need to modify this code and should be able to use it as is. That is to say, you're certainly free to modify the code anyway you need/want, but as is, it should work.
New AppleScript code:
tell current application
try
tell application "Finder"
if running then
set theseFileSystemObjects to (get every item of (path to desktop))
set theFileSystemObjectsNotLocked to {}
repeat with i from 1 to (count of theseFileSystemObjects)
set thisFileSystemObject to (item i of theseFileSystemObjects)
if not locked of (get properties of thisFileSystemObject) then
set end of theFileSystemObjectsNotLocked to thisFileSystemObject
end if
end repeat
if theFileSystemObjectsNotLocked is not equal to {} then
tell current application to set theDateTimeNow to ¬
(do shell script "date '+%m.%d.%Y @ %I.%M.%S %p'") as string
set theDestinationFolder to make new folder at ¬
((path to home folder as string) & "Jimz_Important_Stuff:Desktop_Cleanups:") with properties {name:theDateTimeNow}
move theFileSystemObjectsNotLocked to theDestinationFolder
open theDestinationFolder
activate theDestinationFolder
end if
else
tell current application
activate
display dialog "Finder is not currently running!" & linefeed & linefeed & ¬
"Open Finder, then run Desktop Clean Up again." buttons {"OK"} ¬
default button 1 with title "Desktop Clean Up"
end tell
end if
end tell
on error eStr number eNum
activate
display dialog eStr & " number " & eNum buttons {"OK"} ¬
default button 1 with title "Desktop Clean Up" with icon caution
return
end try
end tell
Original Answer:
This is an example of how I'd write the entire script to be run, as is, as either a .scpt file or an .app file, on your system. I'm saying your system, because the target destination folder is set for your system.
There is no need to load an outside resource and no handler to set the value of the variable used for the destination folder name. I choose to use a do shell script
command for that, using the date
command to return a custom date time string variable, as it's one line of code compared to the amount of code in the on timeandDate()
handler, and it returns the same pattern.
I will continue to use this on my system with appropriately adjusted pathnames and a different pattern for the destination folders name, replacing the /
and :
with .
in the custom date time string variable used in the destination folder name.
As coded, this first version of the script moves all file system objects, that are not locked, currently in the Desktop folder to the destination folder. The script includes minimal appropriate error handling and as coded you shouldn't have the issues you were previously having. This of course assumes that the $HOME/Jimz_Important_Stuff/Desktop_Cleanups
folder exists. If not, you'll get the appropriate error message, however additional error handling could be added to create the hierarchical folded structure as/if necessary.
This first script should resolve all issues you are/were having with your own code and IMO is a better way to code it then just trapping and eating the error with just a try
statement alone, because as coded, it can only move files that are not locked and doesn't even try to move a locked file.
This was tested under macOS 10.12.3 and should work for you on macOS 10.12.5, which you're presently running.
AppleScript Code:
tell current application
try
set theDateTimeNow to (do shell script "date '+%m/%d/%Y @ %I:%M:%S %p'") as string
tell application "Finder"
if running then
close every window
set target of (make new Finder window) to (path to desktop)
activate
delay 0.5
tell application "System Events" to key code 0 using {command down}
delay 0.5
set theseFileSystemObjects to the selection
set theDestinationFolder to make new folder at ¬
((path to home folder as string) & "Jimz_Important_Stuff:Desktop_Cleanups:") with properties {name:theDateTimeNow}
set theListOfFileSystemObjectsNotLocked to {}
repeat with i from 1 to (count of theseFileSystemObjects)
set thisFileSystemObject to (item i of theseFileSystemObjects)
if not locked of thisFileSystemObject then
set end of theListOfFileSystemObjectsNotLocked to thisFileSystemObject
end if
end repeat
move theListOfFileSystemObjectsNotLocked to theDestinationFolder
reveal theDestinationFolder
else
tell current application
activate
display dialog "Finder is not currently running!" & linefeed & linefeed & ¬
"Open Finder, then run Desktop Clean Up again." buttons {"OK"} ¬
default button 1 with title "Desktop Clean Up" with icon caution
end tell
end if
end tell
on error eStr number eNum
activate
display dialog eStr & " number " & eNum buttons {"OK"} ¬
default button 1 with title "Desktop Clean Up" with icon caution
return
end try
end tell
Note: As to the value of the delay
commands, they may need to be adjusted for your system and or additional ones added as/if necessary and or removed if not needed. As presently coded, this script worked without issue, dozens of times in testing on my system, with varying counts and sizes of file system objects being acted upon in the Desktop folder. Make any adjustments as necessary.
The file system objects tested were, files, folders, aliases, symlinks, application bundles and document bundles, the latter two of which are just folders. These FSOs were of various sizes, both locked and not locked.
This second version of the script goes beyond your expressed needs and makes a duplicate of the locked file system objects currently in the Desktop folder to the destination folder, while reseting the locked flag on the duplicates so they can be deleted without raising a flag at the time you may choose to delete them. I'm including it as it may have added value and you find it useful.
AppleScript Code:
tell current application
try
set theDateTimeNow to (do shell script "date '+%m/%d/%Y @ %I:%M:%S %p'") as string
tell application "Finder"
if running then
close every window
set target of (make new Finder window) to (path to desktop)
activate
delay 0.5
tell application "System Events" to key code 0 using {command down}
delay 0.5
set theseFileSystemObjects to the selection
set theDestinationFolder to make new folder at ¬
((path to home folder as string) & "Jimz_Important_Stuff:Desktop_Cleanups:") with properties {name:theDateTimeNow}
set theListOfFileSystemObjectsNotLocked to {}
set theListOfLockedFileSystemObjects to {}
repeat with i from 1 to (count of theseFileSystemObjects)
set thisFileSystemObject to (item i of theseFileSystemObjects)
if not locked of thisFileSystemObject then
set end of theListOfFileSystemObjectsNotLocked to thisFileSystemObject
else
set end of theListOfLockedFileSystemObjects to thisFileSystemObject
end if
end repeat
move theListOfFileSystemObjectsNotLocked to theDestinationFolder
duplicate theListOfLockedFileSystemObjects to theDestinationFolder
repeat with i from 1 to (count of theListOfLockedFileSystemObjects)
set thisFileSystemObject to (item i of theListOfLockedFileSystemObjects)
set locked of alias ((theDestinationFolder as string) & name of thisFileSystemObject) to false
end repeat
reveal theDestinationFolder
else
tell current application
activate
display dialog "Finder is not currently running!" & linefeed & linefeed & ¬
"Open Finder, then run Desktop Clean Up again." buttons {"OK"} ¬
default button 1 with title "Desktop Clean Up" with icon caution
end tell
end if
end tell
on error eStr number eNum
activate
display dialog eStr & " number " & eNum buttons {"OK"} ¬
default button 1 with title "Desktop Clean Up" with icon caution
return
end try
end tell
My approach would be to put some of your code into a try
block and also use on error
to ignore that specific error message, but to still display a message if it encounters a different error instead.
The advantage of this approach is that you're not telling your script to ignore all errors, just in case something else occurs and you should be aware of it.
In order to achieve this, try the following:
set scriptPath to (load script file "Macintosh HD:Users:Smokestack:Library:Mobile Documents:com~apple~ScriptEditor2:Documents:Cleanup Desktop.scptd:Contents:Resources:Scripts:Current Time A.M. P.M. And Short Date.scpt")
tell scriptPath
timeandDate() -- This will get the time and date in this format "05/31/2017 @ 9:10:48 PM" called from the loaded script file above
end tell
set timeandDate to the result -- This will copy the time and date results from the previous step and and set it as this new variable
tell application "Finder"
if running then
close every window
activate
make new Finder window
set target of Finder window 1 to folder "Desktop" of folder "Smokestack" of folder "Users" of startup disk
end if
open Finder window 1
activate
end tell
delay 1
tell application "System Events"
key code 0 using (command down) -- This will select all files and folders on the desktop in the active finder window
end tell
tell application "Finder"
try
set these_items to the selection
set destinationFolder to make new folder at POSIX file "/Users/Smokestack/Jimz_Important_Stuff/Desktop_Cleanups/" with properties {name:timeandDate}
move these_items to destinationFolder
reveal destinationFolder
on error error_message number error_number
if the error_number is not -50 then
display dialog error_message buttons {"OK"} default button 1
end if
end try
end tell
Now, you'll see that I've placed the second block of tell application "Finder"
code within a try
block (you'll notice the use of try
and end try
. And, within that block I've inserted the following code:
on error error_message number error_number
if the error_number is not -50 then
display dialog error_message buttons {"OK"} default button 1
end if
Basically, this should have the effect of telling the script that in the event of a Finder error of -50 to just ignore it, but if it's not -50 then display the error. (I'm assuming that this is the error your script gets - if not you can just replace the -50
with the correct error number.
Obviously I can't test this at my end, so please let me know how you go.