Created
January 20, 2026 15:42
-
-
Save fclairamb/0a30a4bdfbbe8e6ee96c06f13461fa5d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # List installed applications on macOS | |
| # Usage: ./list-apps.sh [--json|--csv] | |
| OUTPUT_FORMAT="text" | |
| # Parse arguments | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --json) | |
| OUTPUT_FORMAT="json" | |
| shift | |
| ;; | |
| --csv) | |
| OUTPUT_FORMAT="csv" | |
| shift | |
| ;; | |
| -h|--help) | |
| echo "Usage: $0 [--json|--csv]" | |
| echo "" | |
| echo "Lists all installed applications on macOS" | |
| echo "" | |
| echo "Options:" | |
| echo " --json Output in JSON format" | |
| echo " --csv Output in CSV format" | |
| echo " --help Show this help message" | |
| exit 0 | |
| ;; | |
| *) | |
| echo "Unknown option: $1" | |
| echo "Use --help for usage information" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # Function to get app info | |
| get_app_info() { | |
| local app_path="$1" | |
| local source="${2:-macOS}" | |
| local app_name=$(basename "$app_path" .app) | |
| local bundle_id="" | |
| local version="" | |
| # Try to get bundle identifier and version from Info.plist | |
| if [ -f "$app_path/Contents/Info.plist" ]; then | |
| bundle_id=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$app_path/Contents/Info.plist" 2>/dev/null || echo "") | |
| version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$app_path/Contents/Info.plist" 2>/dev/null || echo "") | |
| [ -z "$version" ] && version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$app_path/Contents/Info.plist" 2>/dev/null || echo "") | |
| fi | |
| echo "$app_name|$bundle_id|$version|$app_path|$source" | |
| } | |
| # Collect all apps | |
| apps=() | |
| while IFS= read -r -d '' app; do | |
| apps+=("$(get_app_info "$app" "macOS")") | |
| done < <(find /Applications ~/Applications -maxdepth 2 -name "*.app" -print0 2>/dev/null) | |
| # Check if Homebrew is installed and add brew packages | |
| if command -v brew &> /dev/null; then | |
| # Get Homebrew casks (GUI applications) | |
| while IFS= read -r cask; do | |
| [ -z "$cask" ] && continue | |
| # Get cask info | |
| cask_info=$(brew info --cask "$cask" 2>/dev/null | head -n 1) | |
| version=$(echo "$cask_info" | grep -oE '[0-9]+\.[0-9]+[^ ]*' | head -n 1) | |
| [ -z "$version" ] && version=$(brew list --cask --versions "$cask" 2>/dev/null | awk '{print $2}') | |
| # Try to find the actual .app location | |
| app_path="" | |
| for search_dir in /Applications ~/Applications /opt/homebrew/Caskroom/"$cask" /usr/local/Caskroom/"$cask"; do | |
| if [ -d "$search_dir" ]; then | |
| app_found=$(find "$search_dir" -maxdepth 3 -name "*.app" -print -quit 2>/dev/null) | |
| if [ -n "$app_found" ]; then | |
| app_path="$app_found" | |
| break | |
| fi | |
| fi | |
| done | |
| # If we found the .app, get full info; otherwise create entry with basic info | |
| if [ -n "$app_path" ]; then | |
| apps+=("$(get_app_info "$app_path" "brew-cask")") | |
| else | |
| apps+=("$cask||$version|brew-cask|brew-cask") | |
| fi | |
| done < <(brew list --cask 2>/dev/null) | |
| # Get Homebrew formulae (command-line tools) | |
| while IFS= read -r formula; do | |
| [ -z "$formula" ] && continue | |
| version=$(brew list --versions "$formula" 2>/dev/null | awk '{print $2}') | |
| formula_path=$(brew --prefix "$formula" 2>/dev/null) | |
| [ -z "$formula_path" ] && formula_path="brew-formula" | |
| apps+=("$formula||$version|$formula_path|brew-formula") | |
| done < <(brew list --formula 2>/dev/null) | |
| fi | |
| # Sort apps by name | |
| IFS=$'\n' sorted_apps=($(sort <<<"${apps[*]}")) | |
| unset IFS | |
| # Output based on format | |
| case $OUTPUT_FORMAT in | |
| json) | |
| echo "[" | |
| for i in "${!sorted_apps[@]}"; do | |
| IFS='|' read -r name bundle_id version path source <<< "${sorted_apps[$i]}" | |
| echo -n " {\"name\": \"$name\", \"bundle_id\": \"$bundle_id\", \"version\": \"$version\", \"path\": \"$path\", \"source\": \"$source\"}" | |
| if [ $i -lt $((${#sorted_apps[@]} - 1)) ]; then | |
| echo "," | |
| else | |
| echo "" | |
| fi | |
| done | |
| echo "]" | |
| ;; | |
| csv) | |
| echo "Name,Bundle ID,Version,Path,Source" | |
| for app in "${sorted_apps[@]}"; do | |
| IFS='|' read -r name bundle_id version path source <<< "$app" | |
| echo "\"$name\",\"$bundle_id\",\"$version\",\"$path\",\"$source\"" | |
| done | |
| ;; | |
| *) | |
| printf "%-40s %-50s %-15s %-15s\n" "APPLICATION" "BUNDLE ID" "VERSION" "SOURCE" | |
| printf "%-40s %-50s %-15s %-15s\n" "$(printf '%.0s-' {1..40})" "$(printf '%.0s-' {1..50})" "$(printf '%.0s-' {1..15})" "$(printf '%.0s-' {1..15})" | |
| for app in "${sorted_apps[@]}"; do | |
| IFS='|' read -r name bundle_id version path source <<< "$app" | |
| printf "%-40s %-50s %-15s %-15s\n" "$name" "$bundle_id" "$version" "$source" | |
| done | |
| echo "" | |
| echo "Total applications: ${#sorted_apps[@]}" | |
| # Count by source | |
| macos_count=$(echo "${sorted_apps[@]}" | tr ' ' '\n' | grep -c '|macOS$') | |
| cask_count=$(echo "${sorted_apps[@]}" | tr ' ' '\n' | grep -c '|brew-cask$') | |
| formula_count=$(echo "${sorted_apps[@]}" | tr ' ' '\n' | grep -c '|brew-formula$') | |
| echo "" | |
| echo "By source:" | |
| echo " macOS: $macos_count" | |
| echo " Brew Casks: $cask_count" | |
| echo " Brew Formula: $formula_count" | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment