Skip to content

Instantly share code, notes, and snippets.

@gingerbeardman
Last active June 24, 2025 12:44
Show Gist options
  • Save gingerbeardman/610f22180117ad20465d7c529cc5faa0 to your computer and use it in GitHub Desktop.
Save gingerbeardman/610f22180117ad20465d7c529cc5faa0 to your computer and use it in GitHub Desktop.
xbar/bitbar shell script to list and clean and eject (unmount using option) volumes as a replacement for CleanMyDrive
#!/bin/zsh
# <bitbar.title>Volume Manager</bitbar.title>
# <bitbar.version>250624</bitbar.version>
# <bitbar.author>Matt Sephton</bitbar.author>
# <bitbar.author.github>gingerbeardman</bitbar.author.github>
# <bitbar.desc>Lists and manages mounted user volumes</bitbar.desc>
# <bitbar.dependencies>zsh</bitbar.dependencies>
# <bitbar.abouturl>https://gist.github.com/gingerbeardman/610f22180117ad20465d7c529cc5faa0</bitbar.abouturl>
setopt EXTENDED_GLOB
# Function to show notification
show_notification() {
local title=$1
local message=$2
osascript -e "display notification \"$message\" with title \"$title\""
local script=$(basename "$0")
open "xbar://app.xbarapp.com/refreshPlugin?path=$script"
}
# Function to clean and eject/unmount volume
clean_and_process() {
local action=$1
local dev_id=$2
local mount_point=$(diskutil info $dev_id | grep "Mount Point:" | sed 's/.*Mount Point: *//')
local label=$(diskutil info $dev_id | grep -E "Volume Name:" | sed 's/.*Volume Name: *//')
label=${label:-"Untitled"}
# Clean ._ files if mount point exists
if [[ -d "$mount_point" ]]; then
# Remove ._ files
dot_clean -m "$mount_point"
# Remove .DS_Store files
find "$mount_point" -name ".DS_Store" -delete 2>/dev/null
fi
# Perform the requested action
if [[ $action == "eject" ]]; then
if diskutil eject $dev_id; then
show_notification "Volume Ejected" "Successfully ejected $label"
fi
elif [[ $action == "unmount" ]]; then
if diskutil unmount $dev_id; then
show_notification "Volume Unmounted" "Successfully unmounted $label"
fi
fi
}
# Function to eject all volumes (without cleaning)
eject_all() {
# Use while read to properly handle spaces in mount points
local ejected_count=0
mount | grep '^/dev/' | while IFS= read -r vol; do
# Extract device ID (first field)
local dev_id=${vol%% *}
dev_id=${dev_id#/dev/}
# Extract mount point using parameter expansion
local temp=${vol#* on }
local mount_point=${temp%% \(*}
# Skip system volumes and developer paths
[[ -z $mount_point ]] && continue
[[ $mount_point == "/" ]] && continue
[[ $mount_point == /System* ]] && continue
[[ $mount_point == /Library/Developer* ]] && continue
# Direct eject without cleaning
if diskutil eject $dev_id; then
((ejected_count++))
fi
done
if (( ejected_count > 0 )); then
show_notification "Volumes Ejected" "Successfully ejected $ejected_count volumes"
fi
exit 0
}
# Handle actions
if [[ $1 == "eject-all" ]]; then
eject_all
elif [[ $1 == "unmount" || $1 == "eject" ]]; then
clean_and_process $1 $2
exit 0
fi
# Print the menu bar icon and text
print -- "⏏"
print -- "---"
print -- "Refresh | refresh=true"
print -- "---"
# Check if there are valid volumes to eject
local has_valid_volumes=0
# Process each volume using while read to handle spaces properly
mount | grep '^/dev/' | while IFS= read -r vol; do
# Extract device ID (first field before first space)
local dev_id=${vol%% *}
dev_id=${dev_id#/dev/}
# Extract mount point from mount output
# Mount output format: /dev/disk2s1 on /Volumes/My Drive (hfs, local, nodev, nosuid, journaled)
local temp=${vol#* on }
local mount_point=${temp%% \(*}
# Skip system volumes and developer paths
[[ -z $mount_point ]] && continue
[[ $mount_point == "/" ]] && continue
[[ $mount_point == /System* ]] && continue
[[ $mount_point == /Library/Developer* ]] && continue
has_valid_volumes=1
# Get volume label using diskutil
local label=$(diskutil info $dev_id | grep -E "Volume Name:" | sed 's/.*Volume Name: *//')
label=${label:-"Untitled"}
# Print volume info and actions
print -- "${mount_point} | bash='$0' param1=eject param2=$dev_id terminal=false length=40"
print -- "Unmount: ${mount_point} | alternate=true bash='$0' param1=unmount param2=$dev_id terminal=false length=40"
done
# Add Eject All option if there are valid volumes
if (( has_valid_volumes )); then
print -- "---"
print -- "Eject All | bash='$0' param1=eject-all terminal=false color=red"
fi
@gingerbeardman
Copy link
Author

250624: now works with volumes that contain a space character

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment