spotlight-status.sh 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #!/usr/bin/env bash
  2. # mac-ops :: spotlight-status.sh
  3. # Spotlight (mds) health: indexing state per volume, daemon CPU/IO,
  4. # common reindex/repair operations.
  5. set -u
  6. while [[ $# -gt 0 ]]; do
  7. case "$1" in
  8. --help|-h)
  9. cat <<EOF
  10. Usage: $0 [options]
  11. --json, --redact, --quiet, --verbose
  12. Common Spotlight fixes (in order of severity):
  13. 1. Wait — initial indexing on a new volume can take hours
  14. 2. mdutil -E /Volumes/X Erase + rebuild index for a volume
  15. 3. mdutil -i off /Volumes/X Disable Spotlight on a volume entirely
  16. 4. (Reboot — clears mds daemon state)
  17. EOF
  18. exit 0 ;;
  19. *) shift ;;
  20. esac
  21. done
  22. source "$(dirname "$0")/_lib/common.sh"
  23. parse_common_flags "$@"
  24. maybe_filter_self "$@"
  25. # ----------------------------------------------------------------------------
  26. section "1. MDS / MDWORKER PROCESS HEALTH"
  27. # ----------------------------------------------------------------------------
  28. note " Top mds-family processes by CPU:"
  29. ps -ArcS -o pcpu,rss,pid,comm 2>/dev/null | awk '/mds|mdworker|mdsync/' | head -10 | \
  30. awk '{printf " %5s%% RSS=%-8s PID=%s %s\n", $1, $2, $3, $4}'
  31. # Specifically check mds_stores — the kernel-side indexer doing the heavy lifting
  32. mds_cpu=$(ps -ArcS -o pcpu,comm 2>/dev/null | awk '$2=="mds_stores"{print $1; exit}')
  33. mds_cpu="${mds_cpu:-0}"
  34. mds_int=${mds_cpu%.*}
  35. if [[ "${mds_int:-0}" -gt 80 ]]; then
  36. log_warn "mds_stores CPU" "${mds_cpu}% — heavy indexing in progress"
  37. elif [[ "${mds_int:-0}" -gt 30 ]]; then
  38. log_info "mds_stores CPU" "${mds_cpu}% — moderate indexing"
  39. else
  40. log_pass "mds_stores CPU" "${mds_cpu}%"
  41. fi
  42. # ----------------------------------------------------------------------------
  43. section "2. INDEX STATUS PER VOLUME"
  44. # ----------------------------------------------------------------------------
  45. mount | awk '/apfs/{print $3}' | while read -r vol; do
  46. [[ -z "$vol" ]] && continue
  47. # Skip system-managed read-only volumes — they never have indexes
  48. case "$vol" in
  49. /System/Volumes/VM|/System/Volumes/xarts|/System/Volumes/Hardware|/System/Volumes/iSCPreboot|/System/Volumes/Update*) continue ;;
  50. esac
  51. status=$(mdutil -s "$vol" 2>/dev/null | tail -1)
  52. case "$status" in
  53. *"Indexing enabled"*)
  54. log_pass "$vol indexing" "enabled"
  55. ;;
  56. *"Indexing disabled"*)
  57. log_info "$vol indexing" "disabled (Spotlight will not search this volume)"
  58. ;;
  59. *"No index"*|*"not registered"*)
  60. log_warn "$vol indexing" "no index store on disk — search empty until rebuild"
  61. ;;
  62. *"unknown"*)
  63. log_info "$vol indexing" "system volume (no user index)"
  64. ;;
  65. *)
  66. log_info "$vol indexing" "$status"
  67. ;;
  68. esac
  69. done
  70. # ----------------------------------------------------------------------------
  71. section "3. INDEX STORE SIZES"
  72. # ----------------------------------------------------------------------------
  73. note " On-disk Spotlight index size per volume:"
  74. mount | awk '/apfs/{print $3}' | while read -r vol; do
  75. [[ -z "$vol" ]] && continue
  76. spot_dir="$vol/.Spotlight-V100"
  77. if [[ -d "$spot_dir" ]]; then
  78. size=$(du -sh "$spot_dir" 2>/dev/null | awk '{print $1}')
  79. printf " %-30s %s\n" "$vol" "${size:-?}"
  80. fi
  81. done
  82. # ----------------------------------------------------------------------------
  83. section "4. RECENT MDS LOG ACTIVITY"
  84. # ----------------------------------------------------------------------------
  85. mds_errors=$(log show --last 24h --style compact \
  86. --predicate 'process == "mds" OR process == "mds_stores" OR process == "mdworker_shared"' \
  87. 2>/dev/null | grep -iE "(error|fault|crash)" | head -10)
  88. if [[ -n "$mds_errors" ]]; then
  89. log_warn "mds errors (24h)" "see below"
  90. echo "$mds_errors" | sed 's/^/ /'
  91. else
  92. log_pass "mds errors (24h)" "none"
  93. fi
  94. # ----------------------------------------------------------------------------
  95. section "5. INDEX PERMANENT EXCLUSIONS"
  96. # ----------------------------------------------------------------------------
  97. exclusions="$HOME/Library/Preferences/com.apple.spotlight.plist"
  98. if [[ -f "$exclusions" ]]; then
  99. note " Per-user Spotlight preferences exist."
  100. fi
  101. sys_exclusions="/.Spotlight-V100"
  102. if [[ -d "$sys_exclusions" ]]; then
  103. note " Boot volume index dir present at /.Spotlight-V100"
  104. fi
  105. note ""
  106. note " To exclude a path from Spotlight (per-user):"
  107. note " System Settings → Spotlight → Search Privacy → +"
  108. # ----------------------------------------------------------------------------
  109. emit_summary
  110. if [[ "$JSON_MODE" -eq 0 ]]; then
  111. echo
  112. note " Common operations:"
  113. note " mdutil -s /Volumes/X Status per volume"
  114. note " sudo mdutil -E /Volumes/X Erase + rebuild index (heavy operation)"
  115. note " sudo mdutil -i off / Disable Spotlight on boot volume (drastic)"
  116. note " sudo mdutil -i on / Re-enable"
  117. fi