Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

mac-cleanup

A single zsh script that finds reclaimable disk space on a Mac and removes only the things that rebuild themselves. It reports first and deletes nothing until you ask it to, and it asks before every individual deletion.

Typical first run on a developer machine recovers 20 to 40 GB.

What it does

The script measures every known cache location on your Mac, prints them largest first, and then walks the list asking whether to empty each one. It works in three tiers.

Safe tier covers app caches, system logs, crash reports, saved window state, the Trash, the Homebrew download cache, and the downloaded wallpaper cache. All of it regenerates by itself and none of it costs you anything to lose.

Dev tier covers build and package caches: Xcode DerivedData, iOS, watchOS and tvOS device support, the SwiftUI previews cache, CoreDevice data, simulator caches, CocoaPods, Swift Package Manager, Carthage, npm, pnpm, yarn, Gradle, Maven, Cargo, Composer, NuGet, Go modules, pip, Poetry, uv, Bun, Deno, Flutter, RubyGems, Playwright and Puppeteer browser downloads. These also regenerate, but the next build or install has to download again, so the cost is time rather than data.

Electron tier scans every app in Application Support for Chromium cache folders, so Teams, Slack, Notion, VS Code, Cursor, Postman and similar apps get their caches cleared without you naming them. This tier is usually worth several gigabytes on its own and it is the one most cleanup scripts miss.

It also finds node_modules folders that have not been touched in 60 days, anywhere in your home folder, and offers to remove them as a group.

Cache locations are detected rather than assumed. The script asks npm, pnpm, yarn and go where their caches live, and it honours GRADLE_USER_HOME, CARGO_HOME, COMPOSER_HOME, NUGET_PACKAGES and RUSTUP_HOME. Overlapping paths are collapsed so nothing is counted twice.

Two things it prints but never deletes. First, a context section showing your largest home folders, your largest Library folders, and every file over 1 GB, with iCloud placeholder files labelled as such so you do not chase space that is not there. Second, a review list of large items that are not safe to delete blindly, each with the correct way to handle it: simulator devices, Android system images and NDK versions, Docker data, iPhone backups, Mail, Messages, the Photos library and Rust toolchains.

Requirements

macOS, and zsh, which has shipped as the default shell since Catalina. No Homebrew, no dependencies, nothing to install.

How to use it

Download the script, then run it once to see the report:

zsh mac-cleanup.sh

Nothing is deleted by that run. When you are happy with the list, run it again with --clean:

zsh mac-cleanup.sh --clean

The script now stops at each item, tells you what it is and what losing it costs, and waits. Type y and press Enter to empty it, or press Enter to skip. Quit Xcode, your editors and your desktop apps first, since several targets belong to them.

Options:

--clean       Ask about each item and delete the ones you approve
--yes, -y     Answer yes to everything. Combine with --clean
--safe-only   Restrict to the safe tier, skipping build and app caches
--days N      Treat node_modules idle for N days as stale. Default 60
--fast        Skip the whole-home node_modules scan, which is the slow part
--version     Print the version
--help        Print usage

If macOS complains that the file came from an unidentified developer, clear the quarantine flag once:

xattr -d com.apple.quarantine mac-cleanup.sh

Full Disk Access

Terminal does not have access to Desktop, Documents and Downloads by default. Without it the script still runs correctly, but a few locations measure smaller than they really are, so the totals become a floor rather than a ceiling. The script detects this and says so at the top of the report.

To grant it: System Settings, Privacy and Security, Full Disk Access, add Terminal, then quit and reopen Terminal.

Safety

Every target is a cache, a log, or a downloaded artefact that the owning tool re-creates on demand. Nothing containing original work is ever a target.

The script refuses to run as root, because under sudo it would act on the root account's home folder rather than yours, and none of these caches need administrator rights. Every path is verified to sit inside your home folder before it is listed, so even if a tool reports a cache location elsewhere on the system it is ignored. Deletions empty a directory rather than removing it, which keeps permissions and ownership intact. Anything requiring judgement goes in the review list instead of the delete list.

Docker cleanup is limited to docker system prune -f, which removes dangling images and stopped containers. Volumes are never touched, since those hold database contents.

Sample output

Reclaimable locations, largest first:

SIZE         WHAT                               TIER
------------------------------------------------------------
6.5 GB       Xcode iOS DeviceSupport            dev
5.8 GB       Gradle caches                      dev
4.2 GB       User caches                        safe
2.8 GB       npm cache                          dev
740 MB       Xcode previews cache               dev
380 MB       CoreDevice support                 dev
------------------------------------------------------------
Subtotal: 20.4 GB

Desktop app caches, largest first:

SIZE         APP
------------------------------------------------------------
4.5 GB       Microsoft/Teams
1.5 GB       Notion
1.2 GB       Code
------------------------------------------------------------
Subtotal: 7.2 GB

Author

Built by Titus Batson. More work at batsonlabs.com.

If this script freed up a useful amount of space, you can say thanks at buymeacoffee.com/titusbatson.

Licence

MIT.

About

A single zsh script that finds reclaimable disk space on a Mac and removes only the things that rebuild themselves. It reports first and deletes nothing until you ask it to, and it asks before every individual deletion.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages