macOS - Eject hard drives - Macros?
Hi. I have a ton of external hard drives attached to my Mac but sometimes I want to eject them (some of them) to get rid of the noise they generate and when I'm not backing them up via BackBlaze. So is there a way to create some sort of macro/shortcut and then with a press of a button on my StreamDeck eject them all or one by one? Like is it possible to create a macro and include the name of the hard drive and only eject that one? I would then duplicate that macro and just change the name of the drive to get that going for all of them?
- Fannar

Linked from:
- Dustin Harris @Dustin_Harris
I think I wrote something like this a while ago, I’ll check in the morning!
- In reply toFannar_Magnusson⬆:Dustin Harris @Dustin_Harris
Update: I made a 'dynamic deck' which means I can't copy/paste the code here, I'll have to upload it to the store. Let me tweak a few things as it's old code, but I'll let you know once it's up.
- In reply toFannar_Magnusson⬆:Dustin Harris @Dustin_Harris
Turns out I'm going to need to do a lot more work on the dynamic deck version of the Eject Script, as it needs to be refreshed automatically every time an action is taken, or else you can end up trying to eject a drive that has already been ejected.
Here's a script for an on-screen list though, maybe that will be useful?
function getStorageDeviceInfo() { let storageDevicesJSON = sf.system.exec({ commandLine: "system_profiler SPStorageDataType -json" }).result let storageDevices = JSON.parse(storageDevicesJSON).SPStorageDataType //get drive names that are not system data partitions let drives = storageDevices.filter(n => !n._name.match(" - Data") && n.mount_point.match("/Volumes/")).map(n => ({ name: n._name, bsdName: n.bsd_name, })); return drives } /** * @param {string} message */ function notify(message) { sf.interaction.notify({ title: 'SF Eject Drives', message }) } /** * @param {{ name: string; bsdName: string; }} driveToEject */ function doEjectDrive(driveToEject) { let {name, bsdName} = driveToEject; let unmountResult = sf.system.exec({ commandLine: `diskutil unmount ${bsdName}` }); if (unmountResult.exitCode === 0) { notify(`${name} Ejected`) } if (unmountResult.exitCode === 1) { alert('One or more programs prevented the drive from unmounting') } if (unmountResult.exitCode > 1) { alert('An unknown error occurred') } } /** * @param {{name: string, bsdName: string}[]} drives */ function getDriveName(drives) { return sf.interaction.popupSearch({ items: drives.map(n => ({ name: n.name })), }).item.name } function main() { let drives = getStorageDeviceInfo(); let driveNameToEject = getDriveName(drives); let driveToEject = drives.filter(n => n.name === driveNameToEject)[0]; doEjectDrive(driveToEject) } main();
And here's a script I wrote that will eject all removable drives:
function getStorageDeviceInfo() { let storageDevicesJSON = sf.system.exec({ commandLine: "system_profiler SPStorageDataType -json" }).result let storageDevices = JSON.parse(storageDevicesJSON).SPStorageDataType let drives = storageDevices.map(n => ({ name: n._name, bsdName: n.bsd_name, isRemovable: n.physical_drive.is_internal_disk === "no", })); return drives } /** * @param {{ name: string; isRemovable: boolean; bsdName: string; }} drive */ function ejectRemovableDrives(drive) { if (drive.isRemovable) { sf.system.exec({ commandLine: `diskutil unmount ${drive.bsdName}` }).result } } function main() { let drives = getStorageDeviceInfo(); drives.forEach(ejectRemovableDrives); } main();
Hope these are helpful!
Dustin Harris @Dustin_Harris
Here's one more version, where you can specify the drive you want to eject. It will notify you if the drive you're trying to eject isn't mounted.
let driveName = "Your Drive Name Here" function getStorageDeviceInfo() { let storageDevicesJSON = sf.system.exec({ commandLine: "system_profiler SPStorageDataType -json" }).result let storageDevices = JSON.parse(storageDevicesJSON).SPStorageDataType let drives = storageDevices.map(n => ({ name: n._name, bsdName: n.bsd_name, isRemovable: n.physical_drive.is_internal_disk === "no", })); return drives } function main() { let drives = getStorageDeviceInfo(); let listOfMountedDrives = drives.map(n => n.name) if (listOfMountedDrives.indexOf(driveName) == -1) { log(`${driveName} is not mounted`) throw 0; } let {name, bsdName} = drives.filter(n => n.name.match(driveName))[0] let unmountResult = sf.system.exec({ commandLine: `diskutil unmount ${bsdName}` }); if (unmountResult.exitCode === 0) { log(`${name} Ejected`) } else if (unmountResult.exitCode === 1) { alert('One or more programs prevented the drive from unmounting') } else { alert('An unknown error occurred') } } main();
- MMasayoshi Sugai @Masayoshi_Sugai
Thank you, Dustin!
If the storage built into the TC Clarity M Loudness Meter is mounted, the script that mounts and ejects all other drives from soundflow will cause an unexplained error.
I tried to work around this problem by registering the App created by Automator in the Mac's "Login Items" and forcing the Mac to eject the Crality M storage upon startup.
I'll try the script you gave me!Dustin Harris @Dustin_Harris
Yeah, try the last one, it should tell you if an application is using the drive or not.., let me know how it goes :)