Skip to content

Instantly share code, notes, and snippets.

@gingerbeardman
Last active February 25, 2025 13:51
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>250225</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() {
local -a volumes=("${(f)$(mount | grep '^/dev/')}")
local ejected_count=0
for vol in $volumes; do
local mount_point=${${(s: :)vol}[3]}
# Skip system volumes and developer paths
[[ -z $mount_point ]] && continue
[[ $mount_point == "/" ]] && continue
[[ $mount_point == /System* ]] && continue
[[ $mount_point == /Library/Developer* ]] && continue
local dev_id=${${(s: :)vol}[1]}
dev_id=${dev_id#/dev/}
# 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 -- "---"
# Get list of mounted volumes using zsh array
local -a volumes=("${(f)$(mount | grep '^/dev/')}")
# Add Eject All option if there are valid volumes to eject
local has_valid_volumes=0
for vol in $volumes; do
local mount_point=${${(s: :)vol}[3]}
[[ $mount_point == "/" ]] && continue
[[ $mount_point == /System* ]] && continue
[[ $mount_point == /Library/Developer* ]] && continue
if [[ -n $mount_point ]]; then
has_valid_volumes=1
break
fi
done
# Process each volume
for vol in $volumes; do
# Parse mount point using zsh parameter expansion
local mount_point=${${(s: :)vol}[3]}
# Skip system volumes and developer paths using zsh pattern matching
[[ -z $mount_point ]] && continue
[[ $mount_point == "/" ]] && continue
[[ $mount_point == /System* ]] && continue
[[ $mount_point == /Library/Developer* ]] && continue
# Get device ID using zsh parameter expansion
local dev_id=${${(s: :)vol}[1]}
dev_id=${dev_id#/dev/}
# Get volume label using zsh process substitution
local label=$(diskutil info $dev_id | grep -E "Volume Name:" | sed 's/.*Volume Name: *//')
label=${label:-"Untitled"}
# Print volume info and actions, using -- to prevent option parsing
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
if (( has_valid_volumes )); then
print -- "---"
print -- "Eject All | bash='$0' param1=eject-all terminal=false color=red"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment