Empty Trash - (Too Lazy To Crow For Day...)

Hi:

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



I know it’s not in the spirit of AppleScript, but to find the total size of files in your trash you could also use:

set foo to (do shell script "ls -l ~/.Trash | head -1 | awk '{print $2}'") as number
display dialog "Total file size in trash: " & foo & " KB"

Hi Peter,

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

I need to recheck it after dinner.

gl,

Hi Jacques,

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!

gl,

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.

Thanks again.

Peter B.


Hi.

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. :confused:

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’. :wink:

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.

Thanks.

Peter B.


A very nice touch. :slight_smile:

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.

Hi Nigel,

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!

Thanks,

Nigel Garvey wrote:

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.

Peter B.


Hi, Jacques.

That’s great! I’ve been struggling all evening to try and improve it further, but it’s been tough. :slight_smile: 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()

Hi guys.

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_size is 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. :smiley:

So for an alternative challenge, I couldn’t resist the (self-inflicted) torture of trying for a one-liner…

Requires [b]Satimage.osax[/b]


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…
:wink:

So does anyone have an equation or algorithm to calculate the physical mass of a byte?

I’m more than a little out of shape these days. I might have a bit of trouble getting a yottabyte of trash to the curb for pick up.

Conversion to ounces and pounds preferred. I’m not good with that metric stuff.

Peter B.


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.

“Pint’s a pound the world around…”

Thus, by Adam’s example, a yottabyte might weigh as much as a gallon… of water.

Does that give them equivalent density?

Peter B.


Hi, kai.

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. :slight_smile:

US or Imperial?

Nigel Garvey recently wrote:

US or Imperial?

Well sir, as you should know by now, we yankees turn a blind eye to anything that even smacks of imperialism… especially our own.

Peter B.

PS - Sorry to have subverted this thread so lamely.


Mmm… But leaving controversy aside, here’s another approach to the script: :slight_smile:

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!

-MIke