Last active 7 hours ago

darwin-find-app-files.zsh Raw
1#!/usr/bin/env zsh
2
3# Check if an argument was provided
4if [[ -z "$1" ]]; then
5 echo "Usage: $0 /path/to/Application.app"
6 exit 1
7fi
8
9APP_PATH="${1%/}" # Strip any trailing slash for cleaner output
10
11# Validate that the target is a directory ending in .app
12if [[ ! -d "$APP_PATH" || "$APP_PATH" != *.app ]]; then
13 echo "Error: Target must be a valid .app bundle directory."
14 exit 1
15fi
16
17PLIST_PATH="$APP_PATH/Contents/Info.plist"
18APP_NAME=$(basename "$APP_PATH" .app)
19BUNDLE_ID=""
20
21# Step 1: Extract the Bundle Identifier (The unique signature of macOS apps)
22if [[ -f "$PLIST_PATH" ]]; then
23 # Try using 'defaults read' first
24 BUNDLE_ID=$(defaults read "$PLIST_PATH" CFBundleIdentifier 2>/dev/null)
25
26 # Fallback to PlistBuddy if 'defaults' fails (common with binary plists)
27 if [[ -z "$BUNDLE_ID" ]]; then
28 BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$PLIST_PATH" 2>/dev/null)
29 fi
30fi
31
32echo "==================================================="
33echo "Analyzing: $APP_NAME"
34echo "Bundle ID: ${BUNDLE_ID:-Unknown}"
35echo "==================================================="
36
37# Step 2: Print the size of the primary .app bundle itself
38if [[ -e "$APP_PATH" ]]; then
39 du -sh "$APP_PATH"
40fi
41
42# Step 3: Define the search matrix
43# 'typeset -U' ensures array elements are entirely unique, preventing duplicate checks
44typeset -U LOCATIONS
45LOCATIONS=()
46
47# If we successfully extracted a Bundle ID, build the exact paths
48if [[ -n "$BUNDLE_ID" ]]; then
49 LOCATIONS+=(
50 "$HOME/Library/Application Support/$BUNDLE_ID"
51 "$HOME/Library/Caches/$BUNDLE_ID"
52 "$HOME/Library/Preferences/$BUNDLE_ID.plist"
53 "$HOME/Library/Preferences/$BUNDLE_ID.LSSharedFileList.plist"
54 "$HOME/Library/Saved Application State/$BUNDLE_ID.savedState"
55 "$HOME/Library/Logs/$BUNDLE_ID"
56 "$HOME/Library/Containers/$BUNDLE_ID" # Sandboxed App Data
57 "$HOME/Library/HTTPStorages/$BUNDLE_ID" # Network cache
58 "$HOME/Library/HTTPStorages/$BUNDLE_ID.binarycookies"
59 "$HOME/Library/WebKit/$BUNDLE_ID" # WebKit local storage
60 "/Library/Application Support/$BUNDLE_ID" # System-wide App Data
61 "/Library/Caches/$BUNDLE_ID"
62 "/Library/Preferences/$BUNDLE_ID.plist"
63 "/Library/Logs/$BUNDLE_ID"
64 )
65
66 # Handle Group Containers
67 # Group containers are often prefixed with an Apple Developer Team ID (e.g., ABC123DEF4.com.company.app)
68 # The (N) is a zsh glob qualifier meaning "Null Glob" - it won't throw an error if nothing matches.
69 for d in "$HOME/Library/Group Containers/"*${BUNDLE_ID}(N); do
70 LOCATIONS+=("$d")
71 done
72fi
73
74# Add Name-based fallback locations (Many older or non-sandboxed apps just use their localized name)
75LOCATIONS+=(
76 "$HOME/Library/Application Support/$APP_NAME"
77 "$HOME/Library/Caches/$APP_NAME"
78 "$HOME/Library/Logs/$APP_NAME"
79 "$HOME/Library/Preferences/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/${APP_NAME:l}.sfl2" # Recent documents list
80 "/Library/Application Support/$APP_NAME"
81 "/Library/Caches/$APP_NAME"
82 "/Library/Logs/$APP_NAME"
83)
84
85# Step 4: Iterate and calculate sizes
86for target in "${LOCATIONS[@]}"; do
87 if [[ -e "$target" ]]; then
88 # 2>/dev/null suppresses "Permission denied" errors for restricted system folders
89 du -sh "$target" 2>/dev/null
90 fi
91done
92
93echo "==================================================="