I’m having trouble getting past a problem with this script (used as an application)…
If I add multiple items to the trash, then run the script, I often get an error message (on first run) with the script icon (or sometimes the Trash icon) in the dialog saying “Can’t make missing value into type number.”
I’ve tried to ensure the likeliest ‘missing value’ is set… but obviously haven’t succeeded.
Might using ‘size’ from ‘info for’ be a work around?
Thanks.
(Must sleep.)
Peter B.
–
10.4.3
tell application "Finder"
set trash_size to missing value
with timeout of 10 seconds
repeat until trash_size is not missing value
update trash
set trash_size to size of every item in trash
end repeat
end timeout
set trash_count to count of items in trash
if trash_count is 0 then
activate
display dialog return & "There are no items in the Trash." & return buttons {"OK"} default button 1 with title "Trash" with icon note
end if
if trash_count is 1 then
set total_size to trash_size
set size_in_bytes to (round (total_size / 1000))
set size_mag to " KB."
if size_in_bytes is greater than 1000 then
set size_in_bytes to (round (total_size / 1000000))
set size_mag to " MB."
end if
activate
display dialog return & "There is 1 item in the Trash." & return & return & "Its size is " & size_in_bytes & size_mag & return & return buttons {"Cancel", "Empty Trash"} default button 2 with title "Trash" with icon note
set empty_now to button returned of result
if empty_now is "Empty Trash" then
empty trash
end if
end if
if trash_count is greater than 1 then
set total_size to ""
repeat with i in trash_size
set total_size to total_size + i
end repeat
set size_in_bytes to (round (total_size / 1000))
set size_mag to " KB."
if size_in_bytes is greater than 1000 then
set size_in_bytes to (round (total_size / 1000000))
set size_mag to " MB."
end if
activate
display dialog return & "There are " & trash_count & " items in the Trash." & return & return & "Their combined size is " & size_in_bytes & size_mag & return & return buttons {"Cancel", "Empty Trash"} default button 2 with title "Trash" with icon note
set empty_now to button returned of result
if empty_now is "Empty Trash" then
empty trash
end if
end if
end tell
For KB, MB, etc., you need to divide by 1024^n and Finder size is unreliable.
Here’s a quick example:
property byte_units : {"B", "KB", "MB", "GB"}
property num_units : count byte_units
--
set trash_ref to (path to trash as string)
set trash_items to (list folder trash_ref without invisibles)
set trash_size to 0
repeat with this_name in trash_items
set item_ref to (trash_ref & this_name) as alias
set s to size of (info for (item_ref))
set trash_size to trash_size + s
end repeat
repeat with i from 1 to num_units
if trash_size < 1024 ^ i then exit repeat
end repeat
set this_unit to item i of byte_units
set i to i - 1
set trash_size to trash_size / (1024 ^ i)
set size_string to "" & trash_size & this_unit
{size of (info for trash_ref), size_string}
-- size of (info for trash_ref) is in bytes and includes invisibles
Yes, you can’t use path to trash if you want the trash containing trashes for mounted disks. Using the Finder makes it a lot slower though. I wonder if there’s a quick way of getting trash items from every trash of every disk without the Finder.
Thanks for pointing that out.
Darn, I just trashed my file that I put in the trash from my disk image!
Thanks to all of you for your suggestions… I’ll definitely be using some of them in my finished script.
As a fairly recent newcomer to OS X, I’ve found the ‘stock’ trash warning dialog less than helpful… but I was uncomfortable simply turning it off. So… I tried to make an OS 9 - like dialog… but as I often do, got frustrated along the way.
The feeding of items to ‘info for’ can be significantly speeded up in Jacques’s script by getting the Finder to return them as an alias list, rather than coercing the individual Finder references in the repeat loop. Slightly faster still would be to coerce the whole caboodle to a list of Unicode text paths and to use these in file specifiers with the ‘info for’ calls.
Progressing from that idea “ and making the huge assumption that all the trash folders are called “.trash” “ we can instead parse the Unicode paths for the trash folders and perform just one ‘info for’ call for each folder. This returns the same result as Jacques’s script, but about ten times as quickly with my test trash-load:
script o
property thesItems : missing value
end script
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to return as Unicode text
tell application "Finder" to set o's thesItems to paragraphs of (items of trash as Unicode text)
if (o's thesItems is {""}) then
display dialog return & "There are no items in the Trash." & return buttons {"OK"} default button 1 with title "Trash" with icon note
else
set trash_count to (count o's thesItems)
set AppleScript's text item delimiters to ".Trash:" as Unicode text
set trash_containers to {}
set total_size to 0
considering case
repeat with i from 1 to trash_count
set this_container to text item 1 of item i of o's thesItems
if (this_container is in trash_containers) then
else
set end of trash_containers to this_container
set total_size to total_size + (size of (info for file (this_container & ".Trash")))
try
set total_size to total_size - (size of (info for file (this_container & ".Trash:.DS_Store")))
end try
end if
end repeat
end considering
set AppleScript's text item delimiters to astid
set total_size to convertByteSize(total_size)
if (trash_count = 1) then
set t_text to return & "There is 1 item in the Trash." & return & return & "Its size is "
else
set t_text to return & "There are " & trash_count & " items in the Trash." & return & return & "Their combined size is "
end if
display dialog t_text & total_size & return & return buttons {"Cancel", "Empty Trash"} default button 2 with title "Trash" with icon note
if (button returned of the result is "Empty Trash") then tell application "Finder" to empty trash
end if
on convertByteSize(byteSize)
if byteSize ≥ 1.073741824E+9 then
"" & ((byteSize / 1.073741824E+7 div 1) / 100) & " GB"
else if byteSize ≥ 1048576 then
"" & ((byteSize / 1.048576E+4 div 1) / 100) & " MB"
else if byteSize ≥ 1024 then
"" & (byteSize div 1024) & " KB"
else
(byteSize as string) & " bytes"
end if
end convertByteSize
On my machine, shell script results from “ls” and “du” are nearly twice as high as the summed ‘info for’ results or the Finder’s Inspector display. That may be something to do with the difference between actual size and ‘physical size’, but even so, it seems rather high.
Alright, so I was going to post my final script… derivative from examples provided here, though still ‘wordy’, as seems to be my wont.
But after Nigel’s reply, (as usual) I felt kinda ‘dog and pony’.
Instead, I’ll post these bits as replacements for ‘with icon note’ (thanks largely to kai’s example in another thread):
with icon (path to resource “TrashIcon.icns” in bundle (path to me)) – shows empty trash bin
with icon (path to resource “FullTrashIcon.icns” in bundle (path to me)) – shows full trash bin
Both Trash icons can be found in CoreTypes.bundle in /System/Library/Core Services/. Duplicate them into a similar script’s (as application bundle) Resources folder and they will appear in the dialogs.
–
Incidentally (off scripting), does anyone here know how to change the staid blue (or grey) Apple Menu menubar icon? I’d kind of like it to be the ‘traditional’ rainbow again, but haven’t found an appropriate hack.
Since I run most of my scripts from Script Menu as ordinary compiled scripts, I’ve altered the copy on my own computer to read the icons directly from CoreTypes.bundle:
The ‘with title’ in the two dialogs only works in Tiger or later. I think that’s also true for the ability to specify an icon file and the ‘path to resource’ command itself.
I had tried the alias list way and that is what was slowing down the script. The time was cut in half when coercing to string and using that in the info for command. Great tip!
Apologies (if needed) for those who can’t use the added features.
Indeed, I jumped from OS 9 (pretty much) straight to Tiger, so disappointed was I with the early X versions. For a while, I tried to preserve a minimal compatibility in my own scripts between OS versions. No more.
Still being on a PPC machine, and having no plans to upgrade to Intel, I’m staking out a ‘window in time’ that I can use for the forseeable future. Tiger suits me… and Leopard may (or may not) support Classic, so I’m very much in a holding pattern.
As if any of these (i.e my own) meanderings were relevant to the thread.
That’s great! I’ve been struggling all evening to try and improve it further, but it’s been tough. I’ve managed to simplify the code a little in the repeat, but that’s about it. I’ve also renamed the variables ‘trash_containers’ and ‘this_container’ to ‘trashes’ and ‘this_trash’ respectively to reflect their new contents, preset a couple of Unicode text values before the repeat, and put all the main code into a handler to make the variables local.
on main()
script o
property thesItems : missing value
end script
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to return as Unicode text
tell application "Finder" to set o's thesItems to paragraphs of (items of trash as Unicode text)
if (o's thesItems is in {""}) then
display dialog return & "There are no items in the Trash." & return buttons {"OK"} default button 1 with title "Trash" with icon (path to resource "TrashIcon.icns" in bundle file ((path to "csrv" as Unicode text) & "CoreTypes.bundle:"))
else
set trash_count to (count o's thesItems)
set colon to ":" as Unicode text
set AppleScript's text item delimiters to colon
set trashes to {}
set total_size to 0
set DS_Store to ":.DS_Store" as Unicode text
considering case
repeat with i from 1 to trash_count
set this_item to item i of o's thesItems
if (this_item ends with colon) then
set this_trash to text 1 thru text item -3 of this_item
else
set this_trash to text 1 thru text item -2 of this_item
end if
if (this_trash is in trashes) then
else
set end of trashes to this_trash
set total_size to total_size + (size of (info for file this_trash))
try
set total_size to total_size - (size of (info for file (this_trash & DS_Store)))
end try
end if
end repeat
end considering
set AppleScript's text item delimiters to astid
set total_size to convertByteSize(total_size)
if (trash_count = 1) then
set t_text to return & "There is 1 item in the Trash." & return & return & "Its size is "
else
set t_text to return & "There are " & trash_count & " items in the Trash." & return & return & "Their combined size is "
end if
display dialog t_text & total_size & return & return buttons {"Cancel", "Empty Trash"} default button 2 with title "Trash" with icon (path to resource "FullTrashIcon.icns" in bundle file ((path to "csrv" as Unicode text) & "CoreTypes.bundle:"))
if (button returned of the result is "Empty Trash") then tell application "Finder" to empty trash
end if
end main
on convertByteSize(byteSize)
if byteSize ≥ 1.073741824E+9 then
"" & ((byteSize / 1.073741824E+7 div 1) / 100) & " GB"
else if byteSize ≥ 1048576 then
"" & ((byteSize / 1.048576E+4 div 1) / 100) & " MB"
else if byteSize ≥ 1024 then
"" & (byteSize div 1024) & " KB"
else
(byteSize as string) & " bytes"
end if
end convertByteSize
main()
While not wishing to interrupt the current interesting discussion in any way, I thought I’d just throw in a thought or two relating to Peter’s original post.
What you hit initially, Peter, was a known bug, where Finder returns a missing value if it hasn’t previously computed the size of a folder. On receiving a request for an unknown size, Finder says “dunno…” ” and then slopes off to perform the necessary calculations. (That’s why, if the command is tried a second time around, the correct value is usually returned.)
Sidebar: Getting size using Standard Additions:
OK, but that doesn’t really answer your question about why your original script failed ” especially since you’d inserted a repeat loop to catch such an eventuality. Because of an absence, I’ve only just had a chance to take a brief look at the code, and it seems that a tiny oversight may have caused the hiccup.
At the beginning of the script, a missing value is assigned to the variable trash_size:
Next, because trash_sizeis a missing value, the repeat loop is entered, :
Finder is then told to:
This should result in a list; either empty, single-item or multiple-item. The value of the latter might look something like:
The missing value obviously signifies that a folder of indeterminate size is present. However, the repeat loop is exited anyway ” because the value of trash_size is now the list that contains a missing value, rather than the missing value itself.
Depending on how many items are in the trash, the consequences may vary. If there’s only one trashed folder, and its size is not yet known, the value of trash_size will be a single-item list; this would need to be evaluated for the calculation that follows. The attempt would be roughly equivalent to:
(round ({missing value} / 100) / 10)
--> "Can't make missing value into type real."
Where the list contains multiple items, and includes an item of unknown size, the error would occur later, in the summing repeat loop ” as demonstrated by this distilled representation:
set trash_size to {2.25194E+5, 1.7874E+4, 6148.0, 0.0, 1147.0, missing value}
set total_size to 0
repeat with i in trash_size
set total_size to total_size + i
end repeat
--> "Can't make missing value into type number."
Most of these issues could be resolved with a tiny syntax change to the initial comparison operator (the insertion of the containment expression “in”):
Or
Or
Or similar…
The following example incorporates this change, along with one or two other techniques already covered in this discussion. I throw this into the hat merely to demonstrate that your original approach should actually work. (At least, it does here. :))
script trash_object
property size_list : {missing value}
end script
on byte_units(byte_value)
tell byte_value to if it < 1024 then
(it as string) & " bytes"
else if it < 1048576 then
(it div 1024 as string) & " KB"
else if it < 1.073741824E+9 then
(it / 1.048576E+5 div 1 / 10 as string) & " MB"
else
(it / 1.073741824E+8 div 1 / 10 as string) & " GB"
end if
end byte_units
to show_dlog(msg, btns, dflt, icn)
set csrv to (path to "csrv" as Unicode text) & "CoreTypes.bundle"
if (system attribute "sysv") < 4160 then
display dialog msg buttons btns default button dflt with icon path to resource icn in bundle file csrv
else
display dialog msg buttons btns default button dflt with title "Trash" with icon path to resource icn in bundle file csrv
end if
if result's button returned is "Empty Trash" then tell application "Finder" to empty
end show_dlog
tell application "Finder" to set trash_count to count trash's items
if trash_count is 0 then return show_dlog("There are no items in the Trash.", {"OK"}, 1, "TrashIcon.icns")
tell application "Finder" to tell trash's items to repeat while missing value is in trash_object's size_list
set trash_object's size_list to size
end repeat
set total_size to 0
repeat with i from 1 to count trash_object's size_list
set total_size to total_size + (item i of trash_object's size_list)
end repeat
if trash_count is 1 then return show_dlog("There is one item in the Trash." & return & return & "Its size is " & ¬
byte_units(total_size) & ".", {"Cancel", "Empty Trash"}, 2, "FullTrashIcon.icns")
show_dlog("There are " & trash_count & " items in the Trash." & return & return & "Their combined size is " & ¬
byte_units(total_size) & ".", {"Cancel", "Empty Trash"}, 2, "FullTrashIcon.icns")
With something like the byte conversion process involved in this exercise, it would be extremely difficult to beat the efficiency of the hard-coded if/else if/then approach, as so deftly demonstrated earlier by our very own Mr. G.
So for an alternative challenge, I couldn’t resist the (self-inflicted) torture of trying for a one-liner…
on byte_units(v)
tell (ln v) div 6.931471805599 to tell (24 - 15 mod (it + 8)) mod 16 to ((v / (1024 ^ (it - 1)) + 0.05) div 0.1 / 10 as string) & item it of {" bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"}
end byte_units
Of course, if you allow a thousand or so yottabytes of data to accumulate in your trash, the results are likely to be presented in scientific notation form. But then you really should be thinking about clearing out your files a bit more often…
Not as bad as you’d think, Peter. Intel sells 1 Gb flash memory chips for phones - that’s ((1 x 10^9) / 8) bytes - that’s 125 MBytes. How much could one of those weigh - let’s guess one-hundreth of a pound?
Now it’s well known that a yottabyte is 10^11 MBytes so we’d need 800 chips to contain those - only 8 lbs.
Ah “ I’m afraid your script’s still a little vulnerable. For the first few runs with my test trashing of 1649 JPEGS (kept especially for testing scripts with large numbers of files), it reported:
The correct size total, as reported by Jacques’s script and variants, is 376.39 MB. After six or seven runs, your script changed its mind and is now showing 376.3 MB.
Like the ‘with title’ parameter, the ability to use an .icns file with ‘display dialog’'s ‘with icon’ parameter was only introduced with AS 1.10 (ie. with Tiger) “ so your show_dlog() handler should use ‘icon note’ with earlier systems. (I’m going by the release notes here. If it was possible with Panther too, I apologise. It’s certainly not possible with Jaguar.) Also, the two calls to this handler at the bottom of the script should be in an ‘if . else .’ structure.
Mmm… But leaving controversy aside, here’s another approach to the script:
on main()
try
tell application "System Events" to set trashPaths to name of disks where it is local volume and its startup is false
set end of trashPaths to (path to trash as Unicode text)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ":.Trashes:501:" & return as Unicode text
set trashPaths to paragraphs of (trashPaths as Unicode text)
set AppleScript's text item delimiters to astid
on error
set trashPaths to {(path to trash as Unicode text)}
end try
set total_size to 0
set trash_count to 0
set DS_Store to ".DS_Store" as Unicode text
repeat with thisPath in trashPaths
set this_count to (count (list folder file thisPath without invisibles))
if (this_count > 0) then
set trash_count to trash_count + this_count
set total_size to total_size + (size of (info for file thisPath))
try
set total_size to total_size - (size of (info for file (thisPath & DS_Store)))
end try
end if
end repeat
set total_size to convertByteSize(total_size)
if (trash_count is 0) then
display dialog return & "There are no items in the Trash." & return buttons {"OK"} default button 1 with title "Trash" with icon (path to resource "TrashIcon.icns" in bundle file ((path to "csrv" as Unicode text) & "CoreTypes.bundle:"))
else
if (trash_count = 1) then
set t_text to return & "There is 1 item in the Trash." & return & return & "Its size is "
else
set t_text to return & "There are " & trash_count & " items in the Trash." & return & return & "Their combined size is "
end if
display dialog t_text & total_size & return & return buttons {"Cancel", "Empty Trash"} default button 2 with title "Trash" with icon (path to resource "FullTrashIcon.icns" in bundle file ((path to "csrv" as Unicode text) & "CoreTypes.bundle:"))
if (button returned of the result is "Empty Trash") then tell application "Finder" to empty trash
end if
end main
on convertByteSize(byteSize)
if byteSize ≥ 1.073741824E+9 then
"" & ((byteSize / 1.073741824E+7 div 1) / 100) & " GB"
else if byteSize ≥ 1048576 then
"" & ((byteSize / 1.048576E+4 div 1) / 100) & " MB"
else if byteSize ≥ 1024 then
"" & (byteSize div 1024) & " KB"
else
(byteSize as string) & " bytes"
end if
end convertByteSize
main()
Hey Guys. First Off, let me introduce myself, my name is Mike Woodfill, and I read this thread, and thought some of the code would be a great addition to my script. I got code from here, and macosxhints and wrote, what I think is a great script. The link for to program is http://www.netmug.org/~oscar/MoreTrash/
Any questions, concerns, etc can be posted here. Thanks for everything guys!