Last active 9 hours ago

elzi revised this gist 9 hours ago. Go to revision

1 file changed, 93 insertions

darwin-find-app-files.zsh(file created)

@@ -0,0 +1,93 @@
1 + #!/usr/bin/env zsh
2 +
3 + # Check if an argument was provided
4 + if [[ -z "$1" ]]; then
5 + echo "Usage: $0 /path/to/Application.app"
6 + exit 1
7 + fi
8 +
9 + APP_PATH="${1%/}" # Strip any trailing slash for cleaner output
10 +
11 + # Validate that the target is a directory ending in .app
12 + if [[ ! -d "$APP_PATH" || "$APP_PATH" != *.app ]]; then
13 + echo "Error: Target must be a valid .app bundle directory."
14 + exit 1
15 + fi
16 +
17 + PLIST_PATH="$APP_PATH/Contents/Info.plist"
18 + APP_NAME=$(basename "$APP_PATH" .app)
19 + BUNDLE_ID=""
20 +
21 + # Step 1: Extract the Bundle Identifier (The unique signature of macOS apps)
22 + if [[ -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
30 + fi
31 +
32 + echo "==================================================="
33 + echo "Analyzing: $APP_NAME"
34 + echo "Bundle ID: ${BUNDLE_ID:-Unknown}"
35 + echo "==================================================="
36 +
37 + # Step 2: Print the size of the primary .app bundle itself
38 + if [[ -e "$APP_PATH" ]]; then
39 + du -sh "$APP_PATH"
40 + fi
41 +
42 + # Step 3: Define the search matrix
43 + # 'typeset -U' ensures array elements are entirely unique, preventing duplicate checks
44 + typeset -U LOCATIONS
45 + LOCATIONS=()
46 +
47 + # If we successfully extracted a Bundle ID, build the exact paths
48 + if [[ -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
72 + fi
73 +
74 + # Add Name-based fallback locations (Many older or non-sandboxed apps just use their localized name)
75 + LOCATIONS+=(
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
86 + for 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
91 + done
92 +
93 + echo "==================================================="
Newer Older