Looking For a Simpler Way of Doing This

My goal is to eject any mounted DMG Installer. So, I have a script that will list all disks connected to my Mac.

set volName to do shell script "ls /volumes/"
return volName

This gives me the following:
CCC Mac Mini Local
Mac Installs
Mac Mini Local Backups
Macintosh HD
Moneydance Installer
SuperDuper! Clone
SuperDuper! Clone - Data
Sync SSD

I can then use this script to eject only the “Moneydance Installer” (or any other DMG Installer that may be mounted).

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set exceptionsList to {"Macintosh HD", "Mac Installs", "CCC Mac Mini Local", "Mac Mini Local Backups", "SuperDuper! Clone", "SuperDuper! Clone - Data", "Sync SSD"}

considering case
	tell application id "com.apple.finder" to eject (the ¬
		disks whose name is not in my exceptionsList)
end considering

I really think there must be a simple way to target just any DMG Installer, I just can’t seem to come up with the solution, other then just opening the Finder and clicking the eject button. But, I’m trying to improve my AppleScript skills, so I’m going through the exercise.

Use hdiutil with the detach verb. It should be simple enough to incorporate this into an applescript.

% hdiutil attach eco.dmg 
expected   CRC32 $11D64A97
/dev/disk2          	GUID_partition_scheme          	
/dev/disk2s1        	Apple_HFS                      	/Volumes/eco

% hdiutil detach /dev/disk2                         
"disk2" unmounted.
"disk2" ejected.

For actual disks, there is diskutil.

You can use the following to see what is currently mounted/attached. The first will only display disk images. You can then use grep —or contains from within applescript— to extract the needed information.

hdiutil info
diskutil list

Tried your script and I get this error.
Error

In this location.
Script

I did find this shell script (which seems to work) here: How to Eject Mounted DMG Images? - Questions & Suggestions - Keyboard Maestro Discourse

for disk in $(/usr/bin/hdiutil info | /usr/bin/egrep -o "^/dev/disk\d+" | /usr/bin/sort | /usr/bin/uniq); do /usr/bin/hdiutil detach $disk; done

In Terminal, first I used this command to see what I was working with.

df -h

Then I used this following command to filter out the items I do not want to unmount, by grepping for the opposite of any items containing any of the words in the grep command.

df -h |grep -Ev 'System|Filesystem|Recovery|ATC|/$|dev$'

After that produced the results I was looking for, this following command is what I used in Terminal, which unmounted the disc image files which were mounted.

df -h |grep -Ev 'System|Filesystem|Recovery|ATC|/$|dev$' |cut -w -f 9- |tr '\t' ' ' |while read line ;do sudo hdiutil eject -force "$line" ;done

In my sudoers file, I altered it so I do not need to type in my password every time I use the sudo command

Here is the full applescript version

do shell script "df -h |grep -Ev 'System|Filesystem|Recovery|ATC|/$|dev$' |cut -w -f 9- |tr '\t' ' ' |while read line ;do sudo hdiutil eject -force \"$line\" ;done"

Thank you, learned a few things by just going through the various commands. The first thing I learned was to pay close attention. I neglected to add my “Installs HD” (which is an added APFS to my MacBook Pro internal SSD) and ended up ejecting that. No harm as a restart brought it right back.

But, I think I’ll stay with the shell script I posted above, as using it with Automator makes things pretty quick, and simple.

Ahh… you have to remove the ‘%’ sign — that’s the unknown token. That’s not part of the command; it’s actually the prompt. I should have removed it from my post.

If you’re able to add a comment via the “Get Info” panel to each external volume you don’t want ejected, you could do something like this:

tell application "Finder"
	eject (disks whose ejectable is true and group is not "root" and comment is missing value)
end tell

It ejects all ejectable disks not owned by the system and filters out any with a comment (hence, add a comment to any non-system volumes you don’t want ejected). You can add more conditions if needed, but that should cover most bases.

Thanks, I was hopeful that your script would work, but I am unable to add any comment to the main drive, as in “Macintosh HD” without fooling with the permissions, which I don’t want to do. The shell script that I posted works, but I have absolutely no understanding of what any of the symbols mean/do, whereas an AppleScript I can follow.

The main drive should be in the “root” group, so it doesn’t need a comment (same with drives such as recovery and any Xcode simulators).

First, thanks to everyone who responded to this post. I have decided that although the shell script that I found, does in fact work, I have no understanding of how it does what it does, and, at least to me, that is an issue. So I’ve decided to stay with the script that includes the “set exceptionsList to” (posted in post #1) as it being an AppleScript, I can follow and understand what it’s doing.

here is my script to eject dmg disks. it also does iso disks

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

local diskList, tid
set tid to text item delimiters
set diskList to do shell script "diskutil list virtual"
set text item delimiters to "/dev/disk"
set diskList to rest of text items of diskList
set text item delimiters to {" (", ")"}
repeat with aDisk in diskList
	set aDisk to contents of aDisk
	if (text item 2 of aDisk) = "disk image" then do shell script "hdiutil eject disk" & word 1 of aDisk
end repeat
set text item delimiters to tid

Robert, I’m not sure if it’s my system (M1 MacBook Pro, Sonoma 14.7.4) or the script. But, when run, I get nothing for a few seconds,then the spinning beach ball for another few seconds, then the DMG is finally unmounted. Is your script doing a lot of calculations in the background?

No it’s not. The hdiutil takes a bit to execute each run
I’ll try and use diskutil to try and unmount, when I get a chance

** EDIT **
here is the eject line using diskutil

if (text item 2 of aDisk) = "disk image" then do shell script "diskutil unmountDisk disk" & word 1 of aDisk

unfortunately it still takes the same amount of time to eject the disks.

My take on this

use AppleScript version "2.4"
use scripting additions

repeat with thisDMG in Mounted_DMGs()
	tell application "Finder"
		eject disk thisDMG
	end tell
end repeat

on Mounted_DMGs()
	try
		set dmgData to do shell script "hdiutil info"
		set AppleScript's text item delimiters to "================================================" & return
		set dmgData to text items 2 thru -1 of dmgData
		set MountedDMGs to {}
		repeat with i from 1 to length of dmgData
			set thisDMG to item i of dmgData
			set AppleScript's text item delimiters to {"/Volumes/"}
			set thisDMGVolumeName to text item 2 of thisDMG
			set AppleScript's text item delimiters to return
			set the end of MountedDMGs to text item 1 of thisDMGVolumeName
		end repeat
		return MountedDMGs
	on error
		return {}
	end try
end Mounted_DMGs

Paul, your script works extremely well indeed. Any chance you could give me a bit of an explanation on how it does what it does? Just possibly walk me through the script, I’m just learning AppleScript, and every explanation helps.

I am especially curious about this line in the script.

set AppleScript's text item delimiters to "================================================" & return

I’ve never seen something like this in a script. I was experimenting, and this seems to work just as well.

set AppleScript's text item delimiters to "=" & return

So, what’s the purpose of the long string of “=” signs?

I’m parsing the text returned from ‘do shell script “hdiutil info”’.

use AppleScript version "2.4"
use scripting additions--needed because I'm using 'do shell script'

repeat with thisDMG in Mounted_DMGs() --Get and repeat through the list of DMG names.
	tell application "Finder"
		eject disk thisDMG--poof!
	end tell
end repeat

on Mounted_DMGs()
	try
		set dmgData to do shell script "hdiutil info"
		--Get a listing of mounted DMG data
		set AppleScript's text item delimiters to "================================================" & return
		--Header sparator string from hdiutil info
		set dmgData to text items 2 thru -1 of dmgData--remove header
		set MountedDMGs to {}
		repeat with i from 1 to length of dmgData-- loop allows the handler to process multiple mounted DMGs.
			set thisDMG to item i of dmgData
			-->get the first MDGs hdiutil text in the list above. The only entry if only one DMG is mounted.
			set AppleScript's text item delimiters to {"/Volumes/"}
			-->set the delimiters to the point I want to break the string. This occurs in the final line of the text.
			set thisDMGVolumeName to text item 2 of thisDMG
			-->"LogCollectorTool"
			set AppleScript's text item delimiters to return
			set the end of MountedDMGs to text item 1 of thisDMGVolumeName ----Add the results to a list of DMG names
			--using the FIRST text item here, while the delimiters are set to return is so that, 
			--in the case there are more than one reported DMGs I have to remove a trailing 
			--return on all but the final text block. When the text item delimiters do not occur 
			--in the string that you are getting text items of the input string is returned unchanged. 
			--So this line will remove returns from the string when they are present and not 
			--affect the text there it isn''t present.
		end repeat
		return MountedDMGs --> Return the list of DMG names.
	on error
		return {}
	end try
end Mounted_DMGs

Which looks something like this.

framework       : 665.80.2
driver          : 665.80.2
images          : 2
================================================
image-path      : /Users/username/Downloads/Adobe LogCollectorTool.dmg
image-alias     : /Users/username/Downloads/Adobe LogCollectorTool.dmg
shadow-path     : <none>
icon-path       : <none>
image-type      : read-only disk image
system-image    : false
blockcount      : 80696
blocksize       : 512
writeable       : false
autodiskmount   : TRUE
removable       : TRUE
image-encrypted : false
mounting user   : username
mounting mode   : <unknown>
process ID      : 15525
framework name  : DiskImages2
/dev/disk5	Apple_partition_scheme	
/dev/disk5s1	Apple_partition_map	
/dev/disk5s2	Apple_HFS	/Volumes/LogCollectorTool

When you set applescript’s text items to some string and then get the text items of some other string it ‘breaks’ the string at the occurrences of the text item delimiters, returning a list of the text NOT containing the text item delimiters.

Below I’m breaking the text returned from the shell script at the end of the header separator text.
Your use of the final two characters will behave exactly the same as long as there are no other occurrences of “=”&return in the text. I just included the full 48 character string to ensure I didn’t have any issues with unexpected occurrences.

This code should return only the DMG data listings below the header.

set AppleScript's text item delimiters to "================================================" & return
set dmgData to text items 2 thru -1 of dmgData

returns:

{"image-path      : /Users/username/Downloads/Adobe LogCollectorTool.dmg
image-alias     : /Users/username/Downloads/Adobe LogCollectorTool.dmg
shadow-path     : <none>
icon-path       : <none>
image-type      : read-only disk image
system-image    : false
blockcount      : 80696
blocksize       : 512
writeable       : false
autodiskmount   : TRUE
removable       : TRUE
image-encrypted : false
mounting user   : username
mounting mode   : <unknown>
process ID      : 15640
framework name  : DiskImages2
/dev/disk5	Apple_partition_scheme	
/dev/disk5s1	Apple_partition_map	
/dev/disk5s2	Apple_HFS	/Volumes/LogCollectorTool"}

The repeat loop allows the handler to process multiple mounted DMGs.

Inside the repeat loop I’m breaking the text at the string “/Volumes/” which occurs just before the name of the mounted DMG.

repeat with i from 1 to length of dmgData
    set thisDMG to item i of dmgData
-->get the first MDGs hdiutil text in the list above. The only entry if only one DMG is mounted.
    set AppleScript's text item delimiters to {"/Volumes/"}
-->set the delimiters to the point I want to break the string. This occurs in the final line of the text.
    set thisDMGVolumeName to text item 2 of thisDMG
-->"LogCollectorTool"
    set AppleScript's text item delimiters to return
--This is so that, in the case there are more than one reported DMGs I have to remove a trailing return on all but the final text block. When the text item delimiters do not occur in the string that you are getting text items of the input string is returned unchanged. So this line will remove returns from the string when they are present and not affect the tex there it isn''t present.
    set the end of MountedDMGs to text item 1 of thisDMGVolumeName
-->"LogCollectorTool"
end repeat
return MountedDMGs--> Return the list of DMG names.

Then the outer repeat loop repeats with the values returned from the Mounted_DMGs() handler telling the finder to eject them.

repeat with thisDMG in Mounted_DMGs()
	tell application "Finder"
		eject disk thisDMG
	end tell
end repeat

I should add a disclaimer that this type of text parsing is inherently fraught with potential for failure.

1 Like

Thank you for the very detailed explanation of how the script functions. I’ll have to admit that some of it is still a bit over my head, but I’ve copied your entire response into a text document, and I’m sure that over time, I’ll get a better understanding of how it functions to do what it does.