Skip to content

Instantly share code, notes, and snippets.

@fclairamb
Created January 20, 2026 15:42
Show Gist options
  • Select an option

  • Save fclairamb/0a30a4bdfbbe8e6ee96c06f13461fa5d to your computer and use it in GitHub Desktop.

Select an option

Save fclairamb/0a30a4bdfbbe8e6ee96c06f13461fa5d to your computer and use it in GitHub Desktop.
#!/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