Skip to content

Instantly share code, notes, and snippets.

@RussCollis
Last active August 8, 2025 14:29
Show Gist options
  • Select an option

  • Save RussCollis/a0b8d864a84395e3d71bbe9443ee5730 to your computer and use it in GitHub Desktop.

Select an option

Save RussCollis/a0b8d864a84395e3d71bbe9443ee5730 to your computer and use it in GitHub Desktop.
Extracts icons from macOS .app bundles, .pkg installers, and .dmg disk images into PNG format. Logs and summarises extracted, skipped, and missing icons.
#!/bin/bash
# [KPMG] 2025
: <<'SCRIPT_INFO'
=============================================================================
Extract Mac App Icons Script
=============================================================================
Description: Extracts icons from macOS .app bundles, .pkg installers, and
.dmg disk images into PNG format. Logs and summarizes
extracted, skipped, and missing icons with detailed reporting.
Author: Russell Collis (Updated by)
Original: Your Name
Created: 2025-07-08
Updated: 2025-08-08
Version: 1.4.0
Notes: Scans multiple directories for applications and converts icons
using sips. Creates organized output folders and generates
comprehensive summary reports for tracking extraction results.
Requirements: - sips (macOS native image tool)
- pkgutil for .pkg file handling
- hdiutil for .dmg file mounting
- Read/write access to target directories
Output: ~/Desktop/MacAppIcons/ (organized by source type)
Summary: Icon extraction summary with detailed logs
IMPORTANT: This script is specifically designed for KPMG's macOS
environment and should only be deployed within KPMG's managed
infrastructure. It contains organisation-specific
configurations and should not be used outside of this intended
environment.
=============================================================================
=============================================================================
Changelog
=============================================================================
[2025-07-08] v1.4.0 - Enhanced extraction with .pkg and .dmg support
[2025-08-08] v1.4.1 - Applied standardized KPMG header format
=============================================================================
SCRIPT_INFO
# =============================================================
# Configuration Variables
# =============================================================
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_VERSION="1.4.1"
readonly SCRIPT_AUTHOR="Russell Collis"
# === Setup Logging ===
SCRIPT_NAME=$(basename "$0")
CURRENT_DATE=$(date '+%Y-%m-%d')
LOG_FILE="/var/tmp/com.kpmg.${SCRIPT_NAME%.*}_${CURRENT_DATE}.log"
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
log() {
local MESSAGE="$1"
echo "$(date '+%Y-%m-%d %H:%M:%S') [$SCRIPT_NAME] $MESSAGE" | tee -a "$LOG_FILE"
}
# === Output and Summary ===
OUTPUT_FOLDER="$HOME/Desktop/MacAppIcons"
mkdir -p "$OUTPUT_FOLDER"
SUMMARY_FILE="$OUTPUT_FOLDER/_${CURRENT_DATE}_icon_extraction_summary.txt"
> "$SUMMARY_FILE"
ICON_COUNT=0
ICON_COUNT_APP=0
ICON_COUNT_PKG=0
ICON_COUNT_DMG=0
SKIPPED_COUNT=0
MISSING_COUNT=0
# === Application directories ===
APP_DIRS=(
"/Applications"
"/System/Applications"
"/Library/"
"/System/Library/"
"$HOME/Downloads/"
)
log "๐Ÿ“ฆ Extracting icons from .app bundles..."
for DIR in "${APP_DIRS[@]}"; do
while IFS= read -r APP; do
APP_NAME=$(basename "$APP" .app)
SAFE_APP_NAME=$(echo "$APP_NAME" | tr -cd '[:alnum:] _-')
ICON_PATH=$(defaults read "$APP/Contents/Info.plist" CFBundleIconFile 2>/dev/null)
[[ "$ICON_PATH" != *.icns ]] && ICON_PATH="${ICON_PATH}.icns"
FULL_ICON_PATH="$APP/Contents/Resources/$ICON_PATH"
APP_FOLDER="$OUTPUT_FOLDER/$SAFE_APP_NAME"
OUTPUT_ICON="$APP_FOLDER/$SAFE_APP_NAME.png"
if [[ -f "$OUTPUT_ICON" ]]; then
log "โฉ Icon already exists, skipping: $SAFE_APP_NAME"
echo "Skipped (duplicate): $SAFE_APP_NAME" >> "$SUMMARY_FILE"
((SKIPPED_COUNT++))
continue
fi
if [[ -f "$FULL_ICON_PATH" ]]; then
mkdir -p "$APP_FOLDER"
sips -s format png "$FULL_ICON_PATH" --out "$OUTPUT_ICON" >/dev/null 2>&1
((ICON_COUNT++))
((ICON_COUNT_APP++))
log "โœ… Extracted icon for $SAFE_APP_NAME"
echo "Extracted: $SAFE_APP_NAME" >> "$SUMMARY_FILE"
else
log "โŒ Icon file not found for $SAFE_APP_NAME at $FULL_ICON_PATH"
echo "Missing icon: $SAFE_APP_NAME" >> "$SUMMARY_FILE"
((MISSING_COUNT++))
fi
done < <(find "$DIR" -name "*.app" -maxdepth 2 2>/dev/null)
done
# === Extract from .pkg files ===
log "๐Ÿ“ฆ Scanning for .pkg files..."
for DIR in "${APP_DIRS[@]}"; do
while IFS= read -r PKG; do
PKG_NAME=$(basename "$PKG" .pkg)
SAFE_PKG_NAME=$(echo "$PKG_NAME" | tr -cd '[:alnum:] _-')
TEMP_DIR="/tmp/pkg_extract_$SAFE_PKG_NAME"
mkdir -p "$TEMP_DIR"
log "Expanding $PKG to $TEMP_DIR"
pkgutil --expand "$PKG" "$TEMP_DIR" 2>/dev/null || {
log "โŒ Failed to expand $PKG"
rm -rf "$TEMP_DIR"
continue
}
while IFS= read -r ICNS_FILE; do
ICON_NAME=$(basename "$ICNS_FILE" .icns)
OUTPUT_ICON="$OUTPUT_FOLDER/$SAFE_PKG_NAME/${ICON_NAME}.png"
if [[ -f "$OUTPUT_ICON" ]]; then
log "โฉ Icon already exists, skipping: $OUTPUT_ICON"
echo "Skipped (duplicate): $OUTPUT_ICON" >> "$SUMMARY_FILE"
((SKIPPED_COUNT++))
continue
fi
mkdir -p "$(dirname "$OUTPUT_ICON")"
sips -s format png "$ICNS_FILE" --out "$OUTPUT_ICON" >/dev/null 2>&1
((ICON_COUNT++))
((ICON_COUNT_PKG++))
log "โœ… Extracted icon from $PKG: ${ICON_NAME}.png"
echo "Extracted: $SAFE_PKG_NAME - ${ICON_NAME}" >> "$SUMMARY_FILE"
done < <(find "$TEMP_DIR" -name "*.icns")
rm -rf "$TEMP_DIR"
done < <(find "$DIR" -name "*.pkg" -maxdepth 2 2>/dev/null)
done
# === Extract from .dmg files ===
log "๐Ÿ“ฆ Scanning for .dmg files..."
for DIR in "${APP_DIRS[@]}"; do
while IFS= read -r DMG; do
DMG_NAME=$(basename "$DMG" .dmg)
SAFE_DMG_NAME=$(echo "$DMG_NAME" | tr -cd '[:alnum:] _-')
MOUNT_DIR=$(mktemp -d "/Volumes/${SAFE_DMG_NAME}_mnt_XXXX")
log "Mounting $DMG..."
hdiutil attach "$DMG" -mountpoint "$MOUNT_DIR" -nobrowse -quiet
if [[ $? -ne 0 ]]; then
log "โŒ Failed to mount $DMG"
rm -rf "$MOUNT_DIR"
continue
fi
while IFS= read -r ITEM; do
if [[ "$ITEM" == *.app ]]; then
APP_NAME=$(basename "$ITEM" .app)
SAFE_APP_NAME=$(echo "$APP_NAME" | tr -cd '[:alnum:] _-')
ICON_PATH=$(defaults read "$ITEM/Contents/Info.plist" CFBundleIconFile 2>/dev/null)
[[ "$ICON_PATH" != *.icns ]] && ICON_PATH="${ICON_PATH}.icns"
FULL_ICON_PATH="$ITEM/Contents/Resources/$ICON_PATH"
APP_FOLDER="$OUTPUT_FOLDER/${SAFE_DMG_NAME}_${SAFE_APP_NAME}"
OUTPUT_ICON="$APP_FOLDER/${SAFE_APP_NAME}.png"
if [[ -f "$OUTPUT_ICON" ]]; then
log "โฉ Icon already exists, skipping: $OUTPUT_ICON"
echo "Skipped (duplicate): $OUTPUT_ICON" >> "$SUMMARY_FILE"
((SKIPPED_COUNT++))
continue
fi
if [[ -f "$FULL_ICON_PATH" ]]; then
mkdir -p "$APP_FOLDER"
sips -s format png "$FULL_ICON_PATH" --out "$OUTPUT_ICON" >/dev/null 2>&1
((ICON_COUNT++))
((ICON_COUNT_DMG++))
log "โœ… Extracted icon from $DMG: $SAFE_APP_NAME"
echo "Extracted: $SAFE_DMG_NAME - $SAFE_APP_NAME" >> "$SUMMARY_FILE"
else
log "โŒ No icon found in $ITEM"
echo "Missing icon: $ITEM" >> "$SUMMARY_FILE"
((MISSING_COUNT++))
fi
elif [[ "$ITEM" == *.icns ]]; then
ICON_NAME=$(basename "$ITEM" .icns)
OUTPUT_ICON="$OUTPUT_FOLDER/${SAFE_DMG_NAME}/${ICON_NAME}.png"
if [[ -f "$OUTPUT_ICON" ]]; then
log "โฉ Icon already exists, skipping: $OUTPUT_ICON"
echo "Skipped (duplicate): $OUTPUT_ICON" >> "$SUMMARY_FILE"
((SKIPPED_COUNT++))
continue
fi
mkdir -p "$(dirname "$OUTPUT_ICON")"
sips -s format png "$ITEM" --out "$OUTPUT_ICON" >/dev/null 2>&1
((ICON_COUNT++))
((ICON_COUNT_DMG++))
log "โœ… Extracted standalone .icns from $DMG: ${ICON_NAME}.png"
echo "Extracted: $SAFE_DMG_NAME - ${ICON_NAME}" >> "$SUMMARY_FILE"
fi
done < <(find "$MOUNT_DIR" \( -name "*.app" -or -name "*.icns" \) 2>/dev/null)
hdiutil detach "$MOUNT_DIR" -quiet
rm -rf "$MOUNT_DIR"
done < <(find "$DIR" -name "*.dmg" -maxdepth 2 2>/dev/null)
done
# === Final Summary ===
log "๐Ÿงฎ Summary:"
log " โœ”๏ธ Total icons added from scan: $ICON_COUNT"
log " - From .app bundles: $ICON_COUNT_APP"
log " - From .pkg installers: $ICON_COUNT_PKG"
log " - From .dmg images: $ICON_COUNT_DMG"
log " โฉ Icons skipped (duplicates): $SKIPPED_COUNT"
log " โŒ Icons missing or not found: $MISSING_COUNT"
{
echo "======== SUMMARY REPORT ========"
echo "โœ”๏ธ Total icons added from scan: $ICON_COUNT"
echo " - From .app bundles: $ICON_COUNT_APP"
echo " - From .pkg installers: $ICON_COUNT_PKG"
echo " - From .dmg images: $ICON_COUNT_DMG"
echo "โฉ Icons skipped (duplicates): $SKIPPED_COUNT"
echo "โŒ Icons missing or not found: $MISSING_COUNT"
echo "----------------------------------------"
echo ""
echo "Detailed log:"
echo ""
cat "$SUMMARY_FILE"
} > "${SUMMARY_FILE}.tmp" && mv "${SUMMARY_FILE}.tmp" "$SUMMARY_FILE"
log "๐Ÿ“„ Summary report written to: $SUMMARY_FILE"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment