mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 12:41:46 +00:00
Safety and Reliability Improvements
This commit is contained in:
28
bin/clean.sh
28
bin/clean.sh
@@ -152,9 +152,9 @@ cleanup() {
|
||||
INLINE_SPINNER_PID=""
|
||||
fi
|
||||
|
||||
# Clear any spinner output
|
||||
# Clear any spinner output - spinner outputs to stderr
|
||||
if [[ -t 1 ]]; then
|
||||
printf "\r\033[K"
|
||||
printf "\r\033[K" >&2
|
||||
fi
|
||||
|
||||
# Stop sudo session
|
||||
@@ -164,8 +164,8 @@ cleanup() {
|
||||
|
||||
# If interrupted, show message
|
||||
if [[ "$signal" == "INT" ]] || [[ $exit_code -eq 130 ]]; then
|
||||
printf "\r\033[K"
|
||||
echo -e "${YELLOW}Interrupted by user${NC}"
|
||||
printf "\r\033[K" >&2
|
||||
echo -e "${YELLOW}Interrupted by user${NC}" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -271,9 +271,14 @@ safe_clean() {
|
||||
for path in "${existing_paths[@]}"; do
|
||||
(
|
||||
local size
|
||||
size=$(get_path_size_kb "$path")
|
||||
# Timeout protection: prevent du from hanging on problematic paths
|
||||
size=$(run_with_timeout 5 get_path_size_kb "$path")
|
||||
[[ -z "$size" || ! "$size" =~ ^[0-9]+$ ]] && size=0
|
||||
local count
|
||||
count=$(find "$path" -type f 2> /dev/null | wc -l | tr -d ' ')
|
||||
# Timeout protection: prevent find from hanging on problematic paths
|
||||
count=$(run_with_timeout 10 sh -c "find \"$path\" -type f 2> /dev/null | wc -l | tr -d ' '")
|
||||
# If timeout or error, set count to 0 to skip this path
|
||||
[[ -z "$count" || ! "$count" =~ ^[0-9]+$ ]] && count=0
|
||||
# Use index + PID for unique filename
|
||||
local tmp_file="$temp_dir/result_${idx}.$$"
|
||||
echo "$size $count" > "$tmp_file"
|
||||
@@ -330,9 +335,14 @@ safe_clean() {
|
||||
|
||||
for path in "${existing_paths[@]}"; do
|
||||
local size_bytes
|
||||
size_bytes=$(get_path_size_kb "$path")
|
||||
# Timeout protection: prevent du from hanging on problematic paths
|
||||
size_bytes=$(run_with_timeout 5 get_path_size_kb "$path")
|
||||
[[ -z "$size_bytes" || ! "$size_bytes" =~ ^[0-9]+$ ]] && size_bytes=0
|
||||
local count
|
||||
count=$(find "$path" -type f 2> /dev/null | wc -l | tr -d ' ')
|
||||
# Timeout protection: prevent find from hanging on problematic paths
|
||||
count=$(run_with_timeout 10 sh -c "find \"$path\" -type f 2> /dev/null | wc -l | tr -d ' '")
|
||||
# If timeout or error, set count to 0 to skip this path
|
||||
[[ -z "$count" || ! "$count" =~ ^[0-9]+$ ]] && count=0
|
||||
|
||||
if [[ "$count" -gt 0 && "$size_bytes" -gt 0 ]]; then
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
@@ -676,7 +686,7 @@ perform_cleanup() {
|
||||
[[ -f "$app/Contents/Info.plist" ]] || continue
|
||||
bundle_id=$(defaults read "$app/Contents/Info.plist" CFBundleIdentifier 2> /dev/null || echo "")
|
||||
[[ -n "$bundle_id" ]] && echo "$bundle_id" >> "$installed_bundles"
|
||||
done < <(run_with_timeout 10 find "$search_path" -maxdepth 2 -type d -name "*.app" 2> /dev/null || true)
|
||||
done < <(run_with_timeout 10 command find "$search_path" -maxdepth 2 -type d -name "*.app" 2> /dev/null || true)
|
||||
done
|
||||
|
||||
# Get running applications and LaunchAgents with timeout protection
|
||||
|
||||
@@ -183,8 +183,8 @@ scan_applications() {
|
||||
done < <(
|
||||
# Scan both system and user application directories
|
||||
# Using maxdepth 3 to find apps in subdirectories (e.g., Adobe apps in /Applications/Adobe X/)
|
||||
find /Applications -name "*.app" -maxdepth 3 -print0 2> /dev/null
|
||||
find ~/Applications -name "*.app" -maxdepth 3 -print0 2> /dev/null
|
||||
command find /Applications -name "*.app" -maxdepth 3 -print0 2> /dev/null
|
||||
command find ~/Applications -name "*.app" -maxdepth 3 -print0 2> /dev/null
|
||||
)
|
||||
|
||||
# Second pass: process each app with parallel size calculation
|
||||
|
||||
@@ -441,7 +441,7 @@ get_macos_update_labels() {
|
||||
# ============================================================================
|
||||
|
||||
check_disk_space() {
|
||||
local free_gb=$(df -H / | awk 'NR==2 {print $4}' | sed 's/G//')
|
||||
local free_gb=$(command df -H / | awk 'NR==2 {print $4}' | sed 's/G//')
|
||||
local free_num=$(echo "$free_gb" | tr -d 'G' | cut -d'.' -f1)
|
||||
|
||||
export DISK_FREE_GB=$free_num
|
||||
|
||||
@@ -32,7 +32,7 @@ clean_ds_store_tree() {
|
||||
)
|
||||
|
||||
# Build find command to avoid unbound array expansion with set -u
|
||||
local -a find_cmd=("find" "$target")
|
||||
local -a find_cmd=("command" "find" "$target")
|
||||
if [[ "$target" == "$HOME" ]]; then
|
||||
find_cmd+=("-maxdepth" "5")
|
||||
fi
|
||||
@@ -100,7 +100,7 @@ clean_orphaned_app_data() {
|
||||
|
||||
for app_dir in "${app_dirs[@]}"; do
|
||||
[[ -d "$app_dir" ]] || continue
|
||||
find "$app_dir" -name "*.app" -maxdepth 3 -type d 2> /dev/null | while IFS= read -r app_path; do
|
||||
command find "$app_dir" -name "*.app" -maxdepth 3 -type d 2> /dev/null | while IFS= read -r app_path; do
|
||||
local bundle_id
|
||||
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2> /dev/null || echo "")
|
||||
[[ -n "$bundle_id" ]] && echo "$bundle_id"
|
||||
|
||||
@@ -44,7 +44,7 @@ check_tcc_permissions() {
|
||||
# Trigger all TCC prompts upfront by accessing each directory
|
||||
# Using find -maxdepth 1 ensures we touch the directory without deep scanning
|
||||
for dir in "${tcc_dirs[@]}"; do
|
||||
[[ -d "$dir" ]] && find "$dir" -maxdepth 1 -type d > /dev/null 2>&1
|
||||
[[ -d "$dir" ]] && command find "$dir" -maxdepth 1 -type d > /dev/null 2>&1
|
||||
done
|
||||
|
||||
stop_inline_spinner
|
||||
@@ -94,7 +94,7 @@ clean_service_worker_cache() {
|
||||
fi
|
||||
cleaned_size=$((cleaned_size + size))
|
||||
fi
|
||||
done < <(find "$cache_path" -type d -depth 2 2> /dev/null)
|
||||
done < <(command find "$cache_path" -type d -depth 2 2> /dev/null)
|
||||
|
||||
if [[ $cleaned_size -gt 0 ]]; then
|
||||
local cleaned_mb=$((cleaned_size / 1024))
|
||||
@@ -120,7 +120,7 @@ clean_project_caches() {
|
||||
local nextjs_tmp_file
|
||||
nextjs_tmp_file=$(create_temp_file)
|
||||
(
|
||||
find "$HOME" -P -mount -type d -name ".next" -maxdepth 3 \
|
||||
command find "$HOME" -P -mount -type d -name ".next" -maxdepth 3 \
|
||||
-not -path "*/Library/*" \
|
||||
-not -path "*/.Trash/*" \
|
||||
-not -path "*/node_modules/*" \
|
||||
@@ -164,7 +164,7 @@ clean_project_caches() {
|
||||
local pycache_tmp_file
|
||||
pycache_tmp_file=$(create_temp_file)
|
||||
(
|
||||
find "$HOME" -P -mount -type d -name "__pycache__" -maxdepth 3 \
|
||||
command find "$HOME" -P -mount -type d -name "__pycache__" -maxdepth 3 \
|
||||
-not -path "*/Library/*" \
|
||||
-not -path "*/.Trash/*" \
|
||||
-not -path "*/node_modules/*" \
|
||||
|
||||
@@ -49,7 +49,7 @@ clean_broken_preferences() {
|
||||
((broken_count++))
|
||||
((total_size_kb += size_kb))
|
||||
fi
|
||||
done < <(find "$prefs_dir" -maxdepth 1 -name "*.plist" -type f 2> /dev/null || true)
|
||||
done < <(command find "$prefs_dir" -maxdepth 1 -name "*.plist" -type f 2> /dev/null || true)
|
||||
|
||||
# Check ByHost preferences
|
||||
local byhost_dir="$prefs_dir/ByHost"
|
||||
@@ -76,7 +76,7 @@ clean_broken_preferences() {
|
||||
((broken_count++))
|
||||
((total_size_kb += size_kb))
|
||||
fi
|
||||
done < <(find "$byhost_dir" -name "*.plist" -type f 2> /dev/null || true)
|
||||
done < <(command find "$byhost_dir" -name "*.plist" -type f 2> /dev/null || true)
|
||||
fi
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
@@ -153,7 +153,7 @@ clean_broken_login_items() {
|
||||
|
||||
((broken_count++))
|
||||
((total_size_kb += size_kb))
|
||||
done < <(find "$launch_agents_dir" -name "*.plist" -type f 2> /dev/null || true)
|
||||
done < <(command find "$launch_agents_dir" -name "*.plist" -type f 2> /dev/null || true)
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
stop_inline_spinner
|
||||
|
||||
@@ -13,12 +13,12 @@ clean_deep_system() {
|
||||
|
||||
# Clean old temp files
|
||||
local tmp_cleaned=0
|
||||
local tmp_count=$(sudo find /tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
||||
local tmp_count=$(sudo command find /tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
||||
if [[ "$tmp_count" -gt 0 ]]; then
|
||||
safe_sudo_find_delete "/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" || true
|
||||
tmp_cleaned=1
|
||||
fi
|
||||
local var_tmp_count=$(sudo find /var/tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
||||
local var_tmp_count=$(sudo command find /var/tmp -type f -mtime +"${MOLE_TEMP_FILE_AGE_DAYS}" 2> /dev/null | wc -l | tr -d ' ')
|
||||
if [[ "$var_tmp_count" -gt 0 ]]; then
|
||||
safe_sudo_find_delete "/var/tmp" "*" "${MOLE_TEMP_FILE_AGE_DAYS}" "f" || true
|
||||
tmp_cleaned=1
|
||||
@@ -47,7 +47,7 @@ clean_deep_system() {
|
||||
while IFS= read -r -d '' item; do
|
||||
# Skip system-protected files (restricted flag)
|
||||
local item_flags
|
||||
item_flags=$(stat -f%Sf "$item" 2> /dev/null || echo "")
|
||||
item_flags=$(command stat -f%Sf "$item" 2> /dev/null || echo "")
|
||||
if [[ "$item_flags" == *"restricted"* ]]; then
|
||||
continue
|
||||
fi
|
||||
@@ -55,7 +55,7 @@ clean_deep_system() {
|
||||
if safe_sudo_remove "$item"; then
|
||||
((updates_cleaned++))
|
||||
fi
|
||||
done < <(find /Library/Updates -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
||||
done < <(command find /Library/Updates -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
||||
[[ $updates_cleaned -gt 0 ]] && log_success "System library updates"
|
||||
fi
|
||||
fi
|
||||
@@ -103,7 +103,7 @@ clean_time_machine_failed_backups() {
|
||||
fi
|
||||
fi
|
||||
|
||||
local fs_type=$(df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}')
|
||||
local fs_type=$(command df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}')
|
||||
case "$fs_type" in
|
||||
nfs | smbfs | afpfs | cifs | webdav) continue ;;
|
||||
esac
|
||||
@@ -150,7 +150,7 @@ clean_time_machine_failed_backups() {
|
||||
note_activity
|
||||
fi
|
||||
fi
|
||||
done < <(find "$backupdb_dir" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||
done < <(command find "$backupdb_dir" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||
fi
|
||||
|
||||
# APFS style backups (.backupbundle or .sparsebundle)
|
||||
@@ -200,7 +200,7 @@ clean_time_machine_failed_backups() {
|
||||
note_activity
|
||||
fi
|
||||
fi
|
||||
done < <(find "$mounted_path" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||
done < <(command find "$mounted_path" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
@@ -15,7 +15,7 @@ clean_user_essentials() {
|
||||
[[ -d "$volume" && -d "$volume/.Trashes" && -w "$volume" ]] || continue
|
||||
|
||||
# Skip network volumes
|
||||
local fs_type=$(df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}')
|
||||
local fs_type=$(command df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}')
|
||||
case "$fs_type" in
|
||||
nfs | smbfs | afpfs | cifs | webdav) continue ;;
|
||||
esac
|
||||
@@ -26,7 +26,7 @@ clean_user_essentials() {
|
||||
# Safely iterate and remove each item
|
||||
while IFS= read -r -d '' item; do
|
||||
safe_remove "$item" true || true
|
||||
done < <(find "$volume/.Trashes" -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
||||
done < <(command find "$volume/.Trashes" -mindepth 1 -maxdepth 1 -print0 2> /dev/null)
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -65,7 +65,7 @@ clean_finder_metadata() {
|
||||
[[ -d "$volume" && -w "$volume" ]] || continue
|
||||
|
||||
local fs_type=""
|
||||
fs_type=$(df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}')
|
||||
fs_type=$(command df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}')
|
||||
case "$fs_type" in
|
||||
nfs | smbfs | afpfs | cifs | webdav) continue ;;
|
||||
esac
|
||||
@@ -150,7 +150,7 @@ clean_browsers() {
|
||||
[[ "$sw_path" == *"Arc"* ]] && browser_name="Arc"
|
||||
[[ "$profile_name" != "Default" ]] && browser_name="$browser_name ($profile_name)"
|
||||
clean_service_worker_cache "$browser_name" "$sw_path"
|
||||
done < <(find "$HOME/Library/Application Support/Google/Chrome" \
|
||||
done < <(command find "$HOME/Library/Application Support/Google/Chrome" \
|
||||
"$HOME/Library/Application Support/Microsoft Edge" \
|
||||
"$HOME/Library/Application Support/BraveSoftware/Brave-Browser" \
|
||||
"$HOME/Library/Application Support/Arc/User Data" \
|
||||
@@ -250,7 +250,7 @@ clean_application_support_logs() {
|
||||
local profile_name=$(basename "$profile_path")
|
||||
[[ "$profile_name" == "User Data" ]] && profile_name=$(basename "$(dirname "$profile_path")")
|
||||
clean_service_worker_cache "$app_name ($profile_name)" "$sw_cache"
|
||||
done < <(find "$app_dir" -maxdepth 4 -type d \( -name "CacheStorage" -o -name "ScriptCache" \) -path "*/Service Worker/*" 2> /dev/null || true)
|
||||
done < <(command find "$app_dir" -maxdepth 4 -type d \( -name "CacheStorage" -o -name "ScriptCache" \) -path "*/Service Worker/*" 2> /dev/null || true)
|
||||
|
||||
# Clean stale update downloads (older than 7 days)
|
||||
if [[ -d "$app_dir/update" ]] && ls "$app_dir/update" > /dev/null 2>&1; then
|
||||
@@ -259,7 +259,7 @@ clean_application_support_logs() {
|
||||
if [[ $dir_age_days -ge $MOLE_TEMP_FILE_AGE_DAYS ]]; then
|
||||
safe_clean "$update_dir" "Stale update: $app_name"
|
||||
fi
|
||||
done < <(find "$app_dir/update" -mindepth 1 -maxdepth 1 -type d 2> /dev/null || true)
|
||||
done < <(command find "$app_dir/update" -mindepth 1 -maxdepth 1 -type d 2> /dev/null || true)
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -268,17 +268,17 @@ clean_application_support_logs() {
|
||||
while IFS= read -r logs_dir; do
|
||||
local container_name=$(basename "$(dirname "$logs_dir")")
|
||||
safe_clean "$logs_dir"/* "Group container logs: $container_name"
|
||||
done < <(find "$HOME/Library/Group Containers" -maxdepth 2 -type d -name "Logs" 2> /dev/null || true)
|
||||
done < <(command find "$HOME/Library/Group Containers" -maxdepth 2 -type d -name "Logs" 2> /dev/null || true)
|
||||
fi
|
||||
}
|
||||
|
||||
# Check and show iOS device backup info
|
||||
check_ios_device_backups() {
|
||||
local backup_dir="$HOME/Library/Application Support/MobileSync/Backup"
|
||||
if [[ -d "$backup_dir" ]] && find "$backup_dir" -mindepth 1 -maxdepth 1 | read -r _; then
|
||||
if [[ -d "$backup_dir" ]] && command find "$backup_dir" -mindepth 1 -maxdepth 1 | read -r _; then
|
||||
local backup_kb=$(get_path_size_kb "$backup_dir")
|
||||
if [[ -n "${backup_kb:-}" && "$backup_kb" -gt 102400 ]]; then
|
||||
local backup_human=$(du -sh "$backup_dir" 2> /dev/null | awk '{print $1}')
|
||||
local backup_human=$(command du -sh "$backup_dir" 2> /dev/null | awk '{print $1}')
|
||||
note_activity
|
||||
echo -e " Found ${GREEN}${backup_human}${NC} iOS backups"
|
||||
echo -e " You can delete them manually: ${backup_dir}"
|
||||
|
||||
@@ -241,7 +241,7 @@ safe_find_delete() {
|
||||
fi
|
||||
|
||||
# Execute find with safety limits
|
||||
find "$base_dir" \
|
||||
command find "$base_dir" \
|
||||
-maxdepth 3 \
|
||||
-name "$pattern" \
|
||||
-type "$type_filter" \
|
||||
@@ -277,7 +277,7 @@ safe_sudo_find_delete() {
|
||||
fi
|
||||
|
||||
# Execute find with safety limits
|
||||
sudo find "$base_dir" \
|
||||
sudo command find "$base_dir" \
|
||||
-maxdepth 3 \
|
||||
-name "$pattern" \
|
||||
-type "$type_filter" \
|
||||
@@ -386,7 +386,7 @@ detect_architecture() {
|
||||
|
||||
# Get free disk space on root volume (human-readable)
|
||||
get_free_space() {
|
||||
df -h / | awk 'NR==2 {print $4}'
|
||||
command df -h / | awk 'NR==2 {print $4}'
|
||||
}
|
||||
|
||||
# Clear terminal screen and move cursor to home
|
||||
@@ -396,12 +396,16 @@ clear_screen() {
|
||||
|
||||
# Hide terminal cursor
|
||||
hide_cursor() {
|
||||
printf '\033[?25l'
|
||||
[[ -t 1 ]] || return 0
|
||||
# Output to stderr for consistency with spinner, ensure unbuffered
|
||||
printf '\033[?25l' >&2
|
||||
}
|
||||
|
||||
# Show terminal cursor
|
||||
show_cursor() {
|
||||
printf '\033[?25h'
|
||||
[[ -t 1 ]] || return 0
|
||||
# Output to stderr for consistency with spinner, ensure unbuffered
|
||||
printf '\033[?25h' >&2
|
||||
}
|
||||
|
||||
# Read single keypress and return normalized key name
|
||||
@@ -997,7 +1001,13 @@ start_inline_spinner() {
|
||||
# Stop inline spinner
|
||||
stop_inline_spinner() {
|
||||
if [[ -n "$INLINE_SPINNER_PID" ]]; then
|
||||
kill "$INLINE_SPINNER_PID" 2> /dev/null || true
|
||||
# Try graceful TERM first, then force KILL if needed
|
||||
if kill -0 "$INLINE_SPINNER_PID" 2> /dev/null; then
|
||||
kill -TERM "$INLINE_SPINNER_PID" 2> /dev/null || true
|
||||
sleep 0.05 2> /dev/null || true
|
||||
# Force kill if still running
|
||||
kill -KILL "$INLINE_SPINNER_PID" 2> /dev/null || true
|
||||
fi
|
||||
wait "$INLINE_SPINNER_PID" 2> /dev/null || true
|
||||
INLINE_SPINNER_PID=""
|
||||
# Clear the line - use \033[2K to clear entire line, not just to end
|
||||
@@ -1150,7 +1160,7 @@ clean_tool_cache() {
|
||||
get_path_size_kb() {
|
||||
local path="$1"
|
||||
local result
|
||||
result=$(du -sk "$path" 2> /dev/null | awk '{print $1}')
|
||||
result=$(command du -sk "$path" 2> /dev/null | awk '{print $1}')
|
||||
echo "${result:-0}"
|
||||
}
|
||||
|
||||
@@ -1177,22 +1187,36 @@ force_kill_app() {
|
||||
local app_name="$1"
|
||||
local app_path="${2:-}"
|
||||
|
||||
# Use app path for precise matching if provided
|
||||
local match_pattern="$app_name"
|
||||
if [[ -n "$app_path" && -e "$app_path" ]]; then
|
||||
# Use the app bundle path for more precise matching
|
||||
match_pattern="$app_path"
|
||||
# Get the executable name from bundle if app_path is provided
|
||||
local exec_name=""
|
||||
if [[ -n "$app_path" && -e "$app_path/Contents/Info.plist" ]]; then
|
||||
exec_name=$(defaults read "$app_path/Contents/Info.plist" CFBundleExecutable 2> /dev/null || echo "")
|
||||
fi
|
||||
|
||||
if pgrep -f "$match_pattern" > /dev/null 2>&1; then
|
||||
pkill -f "$match_pattern" 2> /dev/null || true
|
||||
sleep 1
|
||||
# Use executable name for precise matching, fallback to app name
|
||||
local match_pattern="${exec_name:-$app_name}"
|
||||
|
||||
# Check if process is actually running using exact match (-x)
|
||||
if ! pgrep -x "$match_pattern" > /dev/null 2>&1; then
|
||||
# Not running, return success
|
||||
return 0
|
||||
fi
|
||||
if pgrep -f "$match_pattern" > /dev/null 2>&1; then
|
||||
pkill -9 -f "$match_pattern" 2> /dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Try graceful termination first
|
||||
pkill -x "$match_pattern" 2> /dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Check again after graceful kill
|
||||
if ! pgrep -x "$match_pattern" > /dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
pgrep -f "$match_pattern" > /dev/null 2>&1 && return 1 || return 0
|
||||
|
||||
# Force kill if still running
|
||||
pkill -9 -x "$match_pattern" 2> /dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Final check
|
||||
pgrep -x "$match_pattern" > /dev/null 2>&1 && return 1 || return 0
|
||||
}
|
||||
|
||||
# Remove application icons from the Dock (best effort)
|
||||
@@ -1791,7 +1815,7 @@ find_app_files() {
|
||||
[[ -f ~/Library/Preferences/"$bundle_id".plist ]] && files_to_clean+=("$HOME/Library/Preferences/$bundle_id.plist")
|
||||
while IFS= read -r -d '' pref; do
|
||||
files_to_clean+=("$pref")
|
||||
done < <(find ~/Library/Preferences/ByHost \( -name "$bundle_id*.plist" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Preferences/ByHost \( -name "$bundle_id*.plist" \) -print0 2> /dev/null)
|
||||
|
||||
# Logs
|
||||
[[ -d ~/Library/Logs/"$app_name" ]] && files_to_clean+=("$HOME/Library/Logs/$app_name")
|
||||
@@ -1800,7 +1824,7 @@ find_app_files() {
|
||||
# Crash Reports and Diagnostics
|
||||
while IFS= read -r -d '' report; do
|
||||
files_to_clean+=("$report")
|
||||
done < <(find ~/Library/Logs/DiagnosticReports \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Logs/DiagnosticReports \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Saved Application State
|
||||
[[ -d ~/Library/Saved\ Application\ State/"$bundle_id".savedState ]] && files_to_clean+=("$HOME/Library/Saved Application State/$bundle_id.savedState")
|
||||
@@ -1811,7 +1835,7 @@ find_app_files() {
|
||||
# Group Containers
|
||||
while IFS= read -r -d '' container; do
|
||||
files_to_clean+=("$container")
|
||||
done < <(find ~/Library/Group\ Containers -type d \( -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Group\ Containers -type d \( -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# WebKit data
|
||||
[[ -d ~/Library/WebKit/"$bundle_id" ]] && files_to_clean+=("$HOME/Library/WebKit/$bundle_id")
|
||||
@@ -1835,7 +1859,7 @@ find_app_files() {
|
||||
# Internet Plug-Ins
|
||||
while IFS= read -r -d '' plugin; do
|
||||
files_to_clean+=("$plugin")
|
||||
done < <(find ~/Library/Internet\ Plug-Ins \( -name "$bundle_id*" -o -name "$app_name*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Internet\ Plug-Ins \( -name "$bundle_id*" -o -name "$app_name*" \) -print0 2> /dev/null)
|
||||
|
||||
# QuickLook Plugins
|
||||
[[ -d ~/Library/QuickLook/"$app_name".qlgenerator ]] && files_to_clean+=("$HOME/Library/QuickLook/$app_name.qlgenerator")
|
||||
@@ -1852,7 +1876,7 @@ find_app_files() {
|
||||
# CoreData
|
||||
while IFS= read -r -d '' coredata; do
|
||||
files_to_clean+=("$coredata")
|
||||
done < <(find ~/Library/CoreData \( -name "*$bundle_id*" -o -name "*$app_name*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/CoreData \( -name "*$bundle_id*" -o -name "*$app_name*" \) -print0 2> /dev/null)
|
||||
|
||||
# Autosave Information
|
||||
[[ -d ~/Library/Autosave\ Information/"$bundle_id" ]] && files_to_clean+=("$HOME/Library/Autosave Information/$bundle_id")
|
||||
@@ -1863,7 +1887,7 @@ find_app_files() {
|
||||
# Receipts (user-level)
|
||||
while IFS= read -r -d '' receipt; do
|
||||
files_to_clean+=("$receipt")
|
||||
done < <(find ~/Library/Receipts \( -name "*$bundle_id*" -o -name "*$app_name*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Receipts \( -name "*$bundle_id*" -o -name "*$app_name*" \) -print0 2> /dev/null)
|
||||
|
||||
# Spotlight Plugins
|
||||
[[ -d ~/Library/Spotlight/"$app_name".mdimporter ]] && files_to_clean+=("$HOME/Library/Spotlight/$app_name.mdimporter")
|
||||
@@ -1871,7 +1895,7 @@ find_app_files() {
|
||||
# Scripting Additions
|
||||
while IFS= read -r -d '' scripting; do
|
||||
files_to_clean+=("$scripting")
|
||||
done < <(find ~/Library/ScriptingAdditions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/ScriptingAdditions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Color Pickers
|
||||
[[ -d ~/Library/ColorPickers/"$app_name".colorPicker ]] && files_to_clean+=("$HOME/Library/ColorPickers/$app_name.colorPicker")
|
||||
@@ -1879,58 +1903,58 @@ find_app_files() {
|
||||
# Quartz Compositions
|
||||
while IFS= read -r -d '' composition; do
|
||||
files_to_clean+=("$composition")
|
||||
done < <(find ~/Library/Compositions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Compositions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Address Book Plug-Ins
|
||||
while IFS= read -r -d '' plugin; do
|
||||
files_to_clean+=("$plugin")
|
||||
done < <(find ~/Library/Address\ Book\ Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Address\ Book\ Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Mail Bundles
|
||||
while IFS= read -r -d '' bundle; do
|
||||
files_to_clean+=("$bundle")
|
||||
done < <(find ~/Library/Mail/Bundles \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Mail/Bundles \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Input Managers (app-specific only)
|
||||
while IFS= read -r -d '' manager; do
|
||||
files_to_clean+=("$manager")
|
||||
done < <(find ~/Library/InputManagers \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/InputManagers \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Custom Sounds
|
||||
while IFS= read -r -d '' sound; do
|
||||
files_to_clean+=("$sound")
|
||||
done < <(find ~/Library/Sounds \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Sounds \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Plugins
|
||||
while IFS= read -r -d '' plugin; do
|
||||
files_to_clean+=("$plugin")
|
||||
done < <(find ~/Library/Plugins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Plugins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Private Frameworks
|
||||
while IFS= read -r -d '' framework; do
|
||||
files_to_clean+=("$framework")
|
||||
done < <(find ~/Library/PrivateFrameworks \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/PrivateFrameworks \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Audio Plug-Ins
|
||||
while IFS= read -r -d '' plugin; do
|
||||
files_to_clean+=("$plugin")
|
||||
done < <(find ~/Library/Audio/Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Audio/Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Components
|
||||
while IFS= read -r -d '' component; do
|
||||
files_to_clean+=("$component")
|
||||
done < <(find ~/Library/Components \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Components \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Metadata
|
||||
while IFS= read -r -d '' metadata; do
|
||||
files_to_clean+=("$metadata")
|
||||
done < <(find ~/Library/Metadata \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Metadata \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Workflows
|
||||
[[ -d ~/Library/Workflows/"$app_name".workflow ]] && files_to_clean+=("$HOME/Library/Workflows/$app_name.workflow")
|
||||
while IFS= read -r -d '' workflow; do
|
||||
files_to_clean+=("$workflow")
|
||||
done < <(find ~/Library/Workflows \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Workflows \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Favorites (excluding Safari)
|
||||
while IFS= read -r -d '' favorite; do
|
||||
@@ -1939,7 +1963,7 @@ find_app_files() {
|
||||
*Safari*) continue ;;
|
||||
esac
|
||||
files_to_clean+=("$favorite")
|
||||
done < <(find ~/Library/Favorites \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find ~/Library/Favorites \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Unix-style configuration directories and files (cross-platform apps)
|
||||
[[ -d ~/.config/"$app_name" ]] && files_to_clean+=("$HOME/.config/$app_name")
|
||||
@@ -1972,7 +1996,7 @@ find_app_system_files() {
|
||||
# Privileged Helper Tools
|
||||
while IFS= read -r -d '' helper; do
|
||||
system_files+=("$helper")
|
||||
done < <(find /Library/PrivilegedHelperTools \( -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/PrivilegedHelperTools \( -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Preferences
|
||||
[[ -f /Library/Preferences/"$bundle_id".plist ]] && system_files+=("/Library/Preferences/$bundle_id.plist")
|
||||
@@ -1980,7 +2004,7 @@ find_app_system_files() {
|
||||
# Installation Receipts
|
||||
while IFS= read -r -d '' receipt; do
|
||||
system_files+=("$receipt")
|
||||
done < <(find /private/var/db/receipts \( -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /private/var/db/receipts \( -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Logs
|
||||
[[ -d /Library/Logs/"$app_name" ]] && system_files+=("/Library/Logs/$app_name")
|
||||
@@ -1989,7 +2013,7 @@ find_app_system_files() {
|
||||
# System Crash Reports and Diagnostics
|
||||
while IFS= read -r -d '' report; do
|
||||
system_files+=("$report")
|
||||
done < <(find /Library/Logs/DiagnosticReports \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Logs/DiagnosticReports \( -name "*$app_name*" -o -name "*$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Frameworks
|
||||
[[ -d /Library/Frameworks/"$app_name".framework ]] && system_files+=("/Library/Frameworks/$app_name.framework")
|
||||
@@ -1997,7 +2021,7 @@ find_app_system_files() {
|
||||
# System Internet Plug-Ins
|
||||
while IFS= read -r -d '' plugin; do
|
||||
system_files+=("$plugin")
|
||||
done < <(find /Library/Internet\ Plug-Ins \( -name "$bundle_id*" -o -name "$app_name*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Internet\ Plug-Ins \( -name "$bundle_id*" -o -name "$app_name*" \) -print0 2> /dev/null)
|
||||
|
||||
# System QuickLook Plugins
|
||||
[[ -d /Library/QuickLook/"$app_name".qlgenerator ]] && system_files+=("/Library/QuickLook/$app_name.qlgenerator")
|
||||
@@ -2005,7 +2029,7 @@ find_app_system_files() {
|
||||
# System Receipts
|
||||
while IFS= read -r -d '' receipt; do
|
||||
system_files+=("$receipt")
|
||||
done < <(find /Library/Receipts \( -name "*$bundle_id*" -o -name "*$app_name*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Receipts \( -name "*$bundle_id*" -o -name "*$app_name*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Spotlight Plugins
|
||||
[[ -d /Library/Spotlight/"$app_name".mdimporter ]] && system_files+=("/Library/Spotlight/$app_name.mdimporter")
|
||||
@@ -2013,7 +2037,7 @@ find_app_system_files() {
|
||||
# System Scripting Additions
|
||||
while IFS= read -r -d '' scripting; do
|
||||
system_files+=("$scripting")
|
||||
done < <(find /Library/ScriptingAdditions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/ScriptingAdditions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Color Pickers
|
||||
[[ -d /Library/ColorPickers/"$app_name".colorPicker ]] && system_files+=("/Library/ColorPickers/$app_name.colorPicker")
|
||||
@@ -2021,32 +2045,32 @@ find_app_system_files() {
|
||||
# System Quartz Compositions
|
||||
while IFS= read -r -d '' composition; do
|
||||
system_files+=("$composition")
|
||||
done < <(find /Library/Compositions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Compositions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Address Book Plug-Ins
|
||||
while IFS= read -r -d '' plugin; do
|
||||
system_files+=("$plugin")
|
||||
done < <(find /Library/Address\ Book\ Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Address\ Book\ Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Mail Bundles
|
||||
while IFS= read -r -d '' bundle; do
|
||||
system_files+=("$bundle")
|
||||
done < <(find /Library/Mail/Bundles \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Mail/Bundles \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Input Managers
|
||||
while IFS= read -r -d '' manager; do
|
||||
system_files+=("$manager")
|
||||
done < <(find /Library/InputManagers \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/InputManagers \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Sounds
|
||||
while IFS= read -r -d '' sound; do
|
||||
system_files+=("$sound")
|
||||
done < <(find /Library/Sounds \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Sounds \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Contextual Menu Items
|
||||
while IFS= read -r -d '' item; do
|
||||
system_files+=("$item")
|
||||
done < <(find /Library/Contextual\ Menu\ Items \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Contextual\ Menu\ Items \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Preference Panes
|
||||
[[ -d /Library/PreferencePanes/"$app_name".prefPane ]] && system_files+=("/Library/PreferencePanes/$app_name.prefPane")
|
||||
@@ -2061,17 +2085,17 @@ find_app_system_files() {
|
||||
# System Audio Plug-Ins
|
||||
while IFS= read -r -d '' plugin; do
|
||||
system_files+=("$plugin")
|
||||
done < <(find /Library/Audio/Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Audio/Plug-Ins \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Components
|
||||
while IFS= read -r -d '' component; do
|
||||
system_files+=("$component")
|
||||
done < <(find /Library/Components \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Components \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# System Extensions
|
||||
while IFS= read -r -d '' extension; do
|
||||
system_files+=("$extension")
|
||||
done < <(find /Library/Extensions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
done < <(command find /Library/Extensions \( -name "$app_name*" -o -name "$bundle_id*" \) -print0 2> /dev/null)
|
||||
|
||||
# Only print if array has elements
|
||||
if [[ ${#system_files[@]} -gt 0 ]]; then
|
||||
|
||||
@@ -207,7 +207,9 @@ opt_mail_downloads() {
|
||||
local deleted=0
|
||||
for target_path in "${mail_dirs[@]}"; do
|
||||
if [[ -d "$target_path" ]]; then
|
||||
local file_count=$(find "$target_path" -type f -mtime "+$MOLE_LOG_AGE_DAYS" 2> /dev/null | wc -l | tr -d ' ')
|
||||
# Timeout protection: prevent find from hanging on large mail directories
|
||||
local file_count=$(run_with_timeout 15 sh -c "find \"$target_path\" -type f -mtime \"+$MOLE_LOG_AGE_DAYS\" 2> /dev/null | wc -l | tr -d ' '")
|
||||
[[ -z "$file_count" || ! "$file_count" =~ ^[0-9]+$ ]] && file_count=0
|
||||
if [[ "$file_count" -gt 0 ]]; then
|
||||
safe_find_delete "$target_path" "*" "$MOLE_LOG_AGE_DAYS" "f"
|
||||
deleted=$((deleted + file_count))
|
||||
@@ -238,7 +240,7 @@ opt_saved_state_cleanup() {
|
||||
if safe_remove "$state_path" true; then
|
||||
((deleted++))
|
||||
fi
|
||||
done < <(find "$state_dir" -type d -name "*.savedState" -mtime "+$MOLE_SAVED_STATE_AGE_DAYS" -print0 2> /dev/null)
|
||||
done < <(command find "$state_dir" -type d -name "*.savedState" -mtime "+$MOLE_SAVED_STATE_AGE_DAYS" -print0 2> /dev/null)
|
||||
|
||||
if [[ $deleted -gt 0 ]]; then
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Removed $deleted old saved state(s)"
|
||||
@@ -448,7 +450,7 @@ get_disk_info() {
|
||||
local home="${HOME:-/}"
|
||||
local df_output total_gb used_gb used_percent
|
||||
|
||||
df_output=$(df -k "$home" 2> /dev/null | tail -1)
|
||||
df_output=$(command df -k "$home" 2> /dev/null | tail -1)
|
||||
|
||||
local total_kb used_kb
|
||||
total_kb=$(echo "$df_output" | awk '{print $2}' 2> /dev/null || echo "0")
|
||||
|
||||
@@ -120,8 +120,13 @@ batch_uninstall_applications() {
|
||||
[[ -z "$selected_app" ]] && continue
|
||||
IFS='|' read -r _ app_path app_name bundle_id _ _ <<< "$selected_app"
|
||||
|
||||
# Check if app is running (use app path for precise matching)
|
||||
if pgrep -f "$app_path" > /dev/null 2>&1; then
|
||||
# Check if app is running using executable name from bundle
|
||||
local exec_name=""
|
||||
if [[ -e "$app_path/Contents/Info.plist" ]]; then
|
||||
exec_name=$(defaults read "$app_path/Contents/Info.plist" CFBundleExecutable 2> /dev/null || echo "")
|
||||
fi
|
||||
local check_pattern="${exec_name:-$app_name}"
|
||||
if pgrep -x "$check_pattern" > /dev/null 2>&1; then
|
||||
running_apps+=("$app_name")
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user