A Zsh script that parses macOS's last log to display a timeline of system reboots, classifying them as intentional (if preceded by a shutdown), and calculating the implied uptime between reboots.
=== Reboot Event Timeline ===
Aug 16 00:42 (13d 12h 53m uptime)
Aug 02 11:49 [intentional] (28d 3h 36m uptime) [macOS Sequoia 15.6]
Jul 05 08:13 [intentional] (20d 8h 12m uptime)
Jun 15 00:01 ( 0d 4h 15m uptime)
Jun 14 19:45 [intentional] (25d 11h 9m uptime)
May 20 08:36 [intentional] ( 7d 22h 31m uptime) [macOS Sequoia 15.5]
May 12 10:05 ( 0d 0h 3m uptime)
May 12 10:02 (15d 21h 14m uptime)
Apr 26 12:48 [intentional] ( 0d 12h 42m uptime) [Command Line Tools for Xcode 16.3, macOS Sequoia 15.4.1]
...
- ✅ Labels reboots as
[intentional]if triggered by user - ✅ Calculates uptime as the time between one reboot and the next
- ✅ Cross-references reboot timestamps with
softwareupdate --historyto identify nearby macOS updates - ✅ Displays multiple software updates per reboot, formatted as
[Update 1, Update 2] - ✅ Removes redundant version numbers from update names for cleaner output
- ✅ Properly handles year rollovers (e.g. Dec 2024 → Jan 2025)
- ✅ Configurable time window for matching reboots to updates (
MATCH_WINDOW_SEC, default 1800s)
- Save the script as
analyze_shutdowns.sh - Make it executable:
chmod +x analyze_shutdowns.sh
- Run it in a Zsh shell:
./analyze_shutdowns.sh
- macOS (tested on Sequoia 15.5–15.6)
- Zsh (default shell in macOS)
- Relies on accurate
lastoutput andsoftwareupdate --history
- Only considers system-level software updates from
softwareupdate(not App Store apps) - Requires recent log retention —
lastmay truncate very old data - Assumes that time zone and system clock were consistent over the observed timeline
- Parsing is sensitive to locale/date formatting; ensure
softwareupdate --historyprints dates asMM/DD/YYYY, HH:MM:SSor the classic split columns. If your locale differs, you may need to adjust thedate -j -fformat string.
- Uses
last | grep -E '^(reboot|shutdown)'to extract reboot and shutdown events only - Determines if a reboot was preceded by a clean shutdown (marking it
[intentional]) - Infers year transitions by monitoring month rollovers
- Calls
softwareupdate --historyto pull all updates and timestamps, accepting either:- combined
MM/DD/YYYY, HH:MM:SSin one column, or - date and time split across adjacent columns
- combined
- Matches each reboot timestamp to all nearby updates (within
MATCH_WINDOW_SEC, default 30 minutes) - Omits version numbers from update names when they are already embedded in the title
- Joins multiple updates as a comma-separated list in square brackets
MIT License — feel free to modify or reuse.
- No updates ever match: increase
MATCH_WINDOW_SEC(e.g., 3600) to account for reboots that happen well after the update finishes. - Weird/empty dates: run
softwareupdate --history | sed -n '1,5p'to inspect actual column layout and date format on your system, then adjust parsing if needed.