Skip to content
Command lineClean up

Cleaning up

Delete the safe tier, choose from what needs review, delete exact paths from a selection file, empty the Trash, and uninstall apps.

On this page

census cleanDeletes

Usage

census clean [PATH ...] [SCOPE] [--safe] [--progress] [--json]

Rescans the scope, deletes the safe findings (caches, build output and logs that rebuild themselves), and saves a review set of everything that needs a person. In a terminal it lists the items and asks [y/N].

OptionDescription
--scope KINDdrive (the default) or data
-p, --path PATHClean only within this folder
--safeDelete without asking. Required without a terminal.
--progressWrite scan and deletion progress as NDJSON to stderr
--jsonWrite one JSON response
Terminal
# List the safe items, ask, delete
census clean

# From a script or an agent, after the person agreed
census clean --safe --json
JSON
{
  "schema_version": 5,
  "command": "clean",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "results": {
    "freed_bytes": 2041856000,
    "used_before_bytes": 50146312192,
    "used_after_bytes": 48104456192,
    "audit": {
      "display": "/Users/me/Library/Application Support/census/audit/clean-1790950000000.ndjson",
      "lossy": false
    },
    "items": [
      {
        "path": { "display": "/Users/me/.npm/_cacache", "lossy": false },
        "rule": "npm.cache",
        "outcome": { "result": "deleted", "bytes": 44953600 }
      },
      {
        "path": { "display": "/Users/me/Library/Caches/com.example.app", "lossy": false },
        "rule": "app.caches",
        "outcome": {
          "result": "skipped",
          "reason": { "kind": "in_use", "detail": "Example is running" }
        }
      },
      "…"
    ],
    "review": {
      "review_id": "rv-1790950000000-1a2b3c4d",
      "items": 14,
      "allocated_bytes": 6300000000
    }
  }
}

used_before_bytes and used_after_bytes are the volume’s used space around the run. With APFS snapshots or clones, used space can fall by less than freed_bytes. Item outcomes lists every result and reason. Only one cleanup runs at a time (cleanup_running), and clean refuses to run as root.

Clean only some safe items

With PATH arguments, only those safe findings are deleted. Write each exactly as census findings --json shows its path. If any one isn’t a safe finding of the fresh scan, the whole run is rejected (invalid_selection) and nothing is deleted.

Terminal
census clean --safe --json "/Users/me/.npm/_cacache"

census reviewDeletes

Usage

census review [SCOPE] [--refresh] [--progress] [--json]

Shows the newest review set for the scope, if it is at most 7 days old, and builds one from a fresh scan if there is none. In a terminal it is a numbered menu: Delete which? (1,3-5 · Enter for none), then Type delete.

OptionDescription
--scope KINDdrive (the default) or data
-p, --path PATHA folder’s own scan
--refreshBuild a new review set from a fresh scan
--progressWith --refresh, write scan progress as NDJSON to stderr
--jsonWrite the review set. Choose from it with census apply.
Example session
$ census review
Needs review · 14 items · 6.3 GB
  1    3.1 GB  ~/proj/foo/target  Cargo build output
  2    1.4 GB  ~/Downloads/Xcode_15.xip  Installer in Downloads
Delete which? (1,3-5 · Enter for none): 2
Permanently delete 1 item (1.4 GB)? Type delete: delete
Freed 1.4 GB · used 44.7 GB → 43.3 GB

review --json only lists. It never deletes:

Terminal
census review --json
JSON
{
  "schema_version": 5,
  "command": "review",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "results": {
    "review_id": "rv-1790950000000-1a2b3c4d",
    "created_at_unix_ms": 1790950000000,
    "scan_id": "scan_1790949990000-41234",
    "items": [
      {
        "path": "/Users/me/proj/foo/target",
        "selectable": true,
        "kind": "folder",
        "rule": "cargo.target",
        "title": "Cargo build output",
        "reason": { "kind": "needs_review" },
        "allocated_bytes": 3100000000
      },
      {
        "path": "/Users/me/Downloads/Xcode_15.xip",
        "selectable": true,
        "kind": "file",
        "rule": "installers.downloads",
        "title": "Installer in Downloads",
        "reason": { "kind": "needs_review" },
        "allocated_bytes": 1400000000
      },
      "…"
    ]
  }
}

path is a plain string here: selection files use exactly these strings. selectable: false marks a path that isn’t valid UTF-8, which only the terminal menu can choose.

census applyDeletes

Usage

census apply FILE [SCOPE] [--progress] [--json]

Deletes the paths a selection file lists. This is how scripts and agents delete what a person chose, without a terminal menu.

OptionDescription
--scope KINDdrive (the default) or data
-p, --path PATHA folder’s own scan
--progressWrite scan and deletion progress as NDJSON to stderr
--jsonWrite one JSON response

Write a selection file

A selection file is a JSON array of paths, copied exactly from one of three listings of the same scope:

  • a review item’s path, from census review --json;
  • a copy’s members[].path.display, from the newest census duplicates search;
  • an item’s items[].path.display, from the newest census large listing.
selection.json
["/Users/me/Downloads/Xcode_15.xip"]
Terminal
census apply selection.json --json

The whole file is rejected, and nothing is deleted, if it isn’t a non-empty array of distinct, absolute, normalized paths, or if any path isn’t in one of those listings (invalid_selection, exit code 1). A listing older than 7 days is review_stale (exit code 1); no listing at all is no_review (exit code 7). Paths you leave out are kept.

What apply checks

apply rescans, then deletes each path only if it still qualifies:

  • A review item must still be a finding of the rule the review set lists. Otherwise it is skipped as missing.
  • A duplicate copy is deleted only if the same search, run again, still finds another copy of its content that stays. If every copy was chosen, or the others changed, it is skipped as no_other_copy. A clone of a copy that stays is deleted with bytes: 0.
  • A Large Files item must still be the same file or folder and pass the same checks. Otherwise it is skipped as missing, changed_since_scan or not_listed.

The result has the same fields as clean‘s, without review.

census empty-trashDeletes

Usage

census empty-trash [PATH ...] [--yes] [--progress] [--json]

Shows what is in every Trash you own, then deletes it permanently after one confirmation. That covers this Mac’s Trash (~/.Trash), iCloud Drive’s, and the Trash on each local, writable drive in /Volumes.

OptionDescription
--yesEmpty without asking. Required without a terminal.
--progressWrite deletion progress as NDJSON to stderr
--jsonWrite one JSON response. Without --yes, it only lists.
Terminal
# Shows the count and size, then asks:
# Permanently delete 1,218 items (8.5 GB)? [y/N]
census empty-trash

In a terminal it deletes exactly the items it showed; anything trashed after that stays. --json without --yes lists and deletes nothing:

Terminal
census empty-trash --json
JSON
{
  "schema_version": 5,
  "command": "empty-trash",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "results": {
    "count": 1218,
    "allocated_bytes": 8512000000,
    "trashes": [
      {
        "name": "This Mac",
        "path": { "display": "/Users/me/.Trash", "lossy": false },
        "count": 1190,
        "allocated_bytes": 8200000000,
        "items": [
          {
            "path": { "display": "/Users/me/.Trash/Xcode_15.xip", "lossy": false },
            "kind": "file",
            "allocated_bytes": 6900000000,
            "trashed_unix_ms": 1790950000000
          },
          "…"
        ]
      },
      {
        "name": "iCloud Drive",
        "path": { "display": "/Users/me/Library/Mobile Documents/com~apple~CloudDocs/.Trash", "lossy": false },
        "count": 14,
        "allocated_bytes": 12000000
      }
    ]
  }
}

To delete only some of it, pass the items’ items[].path or whole Trashes’ trashes[].path as arguments, exactly as --json lists them:

Terminal
census empty-trash --yes --json "/Users/me/.Trash/Xcode_15.xip"
  • Nothing is forced. Locked items, items an app has open (in_use), and folders another user owns stay, and are listed.
  • A Trash census can’t read stops the command before anything is deleted (trash_unreadable, exit code 2). Reading ~/.Trash needs Full Disk Access for the app that runs census.
  • iCloud items that are only in the cloud are deleted without being downloaded.

--yes lists the Trashes again before deleting, so it deletes what is there by then, as Finder does. With --yes --json, the result is clean‘s without review, with rule: "user.trash" on every item.

census uninstallDeletes

Usage

census uninstall NAME [PATH ...] [SCOPE] [--yes] [--progress] [--json]

Rescans, then removes an app and the data named after it. NAME matches as census app does, by name or bundle ID. In a terminal it lists what goes and asks [y/N].

OptionDescription
--scope KINDdrive (the default) or data
-p, --path PATHA folder’s own scan
--yesUninstall without asking. Required without a terminal.
--progressWrite scan and deletion progress as NDJSON to stderr
--jsonWrite one JSON response
Terminal
# See what an uninstall would remove
census app Slack

# Remove the app and its data, after one confirmation
census uninstall Slack
  • What goes. The app, and its data in private locations: folders named after its bundle ID, its helper’s ID, or its name, with high or medium confidence, not shared, and not needing an administrator. With PATH arguments, exactly those paths go, as census app NAME --json shows them, and the app itself must be one of them.
  • What stays. Everything else, listed in results.kept with a reason: shared (a group container other apps from the developer may use), needs_admin (under /Library), may_belong_to_another_app, not_selected or not_verifiable.
  • Before anything is deleted. A part of macOS is refused (system_app). The app’s launch agents in ~/Library/LaunchAgents are unloaded. If a program inside the app is still running, nothing is deleted (app_running): census never quits apps.
  • Order. The app goes first. Its data goes only if the app was deleted in full.
Terminal
census uninstall Slack --yes --json
JSON
{
  "schema_version": 5,
  "command": "uninstall",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "results": {
    "app": {
      "name": "Slack",
      "bundle_id": "com.tinyspeck.slackmacgap",
      "version": "4.41",
      "path": { "display": "/Applications/Slack.app", "lossy": false }
    },
    "freed_bytes": 612000000,
    "used_before_bytes": 50146312192,
    "used_after_bytes": 49534312192,
    "audit": {
      "display": "/Users/me/Library/Application Support/census/audit/uninstall-1790950000000.ndjson",
      "lossy": false
    },
    "items": [
      {
        "path": { "display": "/Applications/Slack.app", "lossy": false },
        "rule": "app.bundle",
        "outcome": { "result": "deleted", "bytes": 560000000 }
      },
      {
        "path": { "display": "/Users/me/Library/Caches/com.tinyspeck.slackmacgap", "lossy": false },
        "rule": "app.data",
        "outcome": { "result": "deleted", "bytes": 52000000 }
      }
    ],
    "kept": [
      {
        "path": {
          "display": "/Users/me/Library/Group Containers/BQR82RBBHL.com.tinyspeck.slackmacgap",
          "lossy": false
        },
        "location": "Shared group container",
        "confidence": "medium",
        "allocated_bytes": 310000000,
        "reason": "shared"
      }
    ],
    "background_items": [],
    "not_inspected": []
  }
}

If the app isn’t deleted in full, the exit code is 4 with app_not_removed, and its data is left as it was. EPERM on the app usually means macOS’s App Management protection: allow the app that runs census in System Settings › Privacy & Security.

Updated · census JSON schema 5