cheat sheet

system_profiler

Deep-dive on system_profiler(8) — macOS's built-in inventory tool. Covers data types, text/XML/JSON output, piping into jq and plutil, common SP* probes (hardware, displays, memory, storage, USB, network, software), detail levels, and fleet inventory recipes.

system_profiler — macOS Hardware & Software Inventory

What it is

system_profiler(8) is the command-line equivalent of the macOS GUI app System Information (formerly "About this Mac → System Report"). It dumps a structured inventory of everything the OS knows about the machine — CPU, RAM, displays, internal and external storage, USB topology, network interfaces, Wi-Fi, audio, printers, installed software, kernel extensions, applications, even environment variables — partitioned into ~80 named "data types" you query individually. It ships with macOS at /usr/sbin/system_profiler and runs as a regular user (some data types require root). Reach for it when you need a complete machine-readable snapshot for fleet inventory, asset management imports, support tickets, or to discover a specific model identifier / serial / firmware version from a script. On Windows, the closest analogue is systeminfo + Get-ComputerInfo; on Linux, dmidecode + lshw.

Install

system_profiler ships with every macOS install at /usr/sbin/system_profiler — nothing to install. The GUI counterpart, System Information, lives at /System/Library/CoreServices/Applications/System Information.app and shares the same backend.

bash
which system_profiler
system_profiler -h 2>&1 | head -10

Output:

text
/usr/sbin/system_profiler
Usage: system_profiler [-listDataTypes]
       system_profiler [-xml | -json] [-detailLevel level] [datatype1 ... datatypeN]
       system_profiler [-xml | -json] -timeout n -detailLevel level
       system_profiler -listDataTypes
       system_profiler -usage

Syntax

The bare form (no arguments) dumps every data type at the default detail level — useful, but slow (~30–60 seconds) and verbose (10–50 MB of text). For day-to-day work, query a single data type (SPHardwareDataType, SPDisplaysDataType), choose a detail level, and pick a machine-readable output format.

bash
system_profiler [DATATYPE...]                          # plain text dump
system_profiler -xml [DATATYPE...]                     # plist XML
system_profiler -json [DATATYPE...]                    # JSON (macOS 12+)
system_profiler -listDataTypes                         # available data types
system_profiler -detailLevel mini|basic|full DATATYPE  # control verbosity
system_profiler -timeout SECONDS                       # cap collection time

Output: (none — exits 0 on success)

Essential options

OptionMeaning
-listDataTypesList every data type the system supports (~80 entries)
-xmlOutput a binary plist as XML (suitable for plutil)
-jsonOutput JSON — macOS 12+ only (suitable for jq)
-detailLevel miniSmallest output; coarse counts, no nested arrays
-detailLevel basicDefault for most data types
-detailLevel fullMost verbose — adds binary blobs, firmware paths, S/N
-timeout SECONDSForce-finish if a probe takes too long (default ~10s per type)
-usagePer-data-type detail-level help: system_profiler SPHardwareDataType -usage
-nospawnRun probes in-process (no per-type subprocesses)

Listing available data types

-listDataTypes enumerates everything queryable. The set is essentially fixed across modern macOS but grows slightly with new Apple Silicon generations (e.g. SPSecureElementDataType appeared with Touch ID Macs). Each data type follows the SP*DataType naming convention.

bash
system_profiler -listDataTypes

Output:

text
Available Datatypes:
SPParallelATADataType
SPUniversalAccessDataType
SPSecureElementDataType
SPApplicationsDataType
SPAudioDataType
SPBluetoothDataType
SPCameraDataType
SPCardReaderDataType
SPiBridgeDataType
SPDeveloperToolsDataType
SPDiagnosticsDataType
SPDisabledSoftwareDataType
SPDiscBurningDataType
SPEthernetDataType
SPExtensionsDataType
SPFibreChannelDataType
SPFireWireDataType
SPFirewallDataType
SPFontsDataType
SPFrameworksDataType
SPDisplaysDataType
SPHardwareDataType
SPInstallHistoryDataType
SPInternationalDataType
SPLegacySoftwareDataType
SPNetworkLocationDataType
SPLogsDataType
SPManagedClientDataType
SPMemoryDataType
SPNVMeDataType
SPNetworkDataType
SPPCIDataType
SPParallelSCSIDataType
SPPowerDataType
SPPrefPaneDataType
SPPrintersSoftwareDataType
SPPrintersDataType
SPConfigurationProfileDataType
SPRawCameraDataType
SPSASDataType
SPSerialATADataType
SPSPIDataType
SPSmartCardsDataType
SPSoftwareDataType
SPStartupItemDataType
SPStorageDataType
SPSyncServicesDataType
SPThunderboltDataType
SPUSBDataType
SPNetworkVolumeDataType
SPWWANDataType
SPAirPortDataType

The most commonly used 10 (in my experience):

Data typeWhat it reports
SPHardwareDataTypeModel, chip, cores, RAM total, serial number, hardware UUID
SPSoftwareDataTypemacOS version, build, kernel, boot mode, user/computer name
SPDisplaysDataTypeGraphics controllers + every connected display (res, EDID)
SPMemoryDataTypeTotal RAM, ECC state, manufacturer; per-DIMM detail on Intel Macs
SPStorageDataTypeEach mounted volume: type, BSD name, capacity, free space, role
SPNetworkDataTypeEvery network service (Wi-Fi, Ethernet, VPN) with IP/route/DNS
SPUSBDataTypeFull USB topology (hubs + downstream devices)
SPBluetoothDataTypeBT controller + paired/connected devices
SPApplicationsDataTypeEvery installed .app with path, version, signature
SPPowerDataTypeBattery state, AC adapter wattage, sleep settings

Plain text output

The default format is human-readable indented text — the same content you'd see in System Information.app. Suitable for support tickets, but a chore to parse.

bash
system_profiler SPHardwareDataType

Output:

text
Hardware:

    Hardware Overview:

      Model Name: MacBook Pro
      Model Identifier: Mac14,9
      Model Number: MNW93LL/A
      Chip: Apple M2 Pro
      Total Number of Cores: 10 (6 performance and 4 efficiency)
      Memory: 16 GB
      System Firmware Version: 10151.121.1
      OS Loader Version: 10151.121.1
      Serial Number (system): XXXXXXXXXXX
      Hardware UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
      Provisioning UDID: XXXXXXXX-XXXXXXXXXXXXXXXX
      Activation Lock Status: Enabled
bash
system_profiler SPSoftwareDataType

Output:

text
Software:

    System Software Overview:

      System Version: macOS 15.5 (24F74)
      Kernel Version: Darwin 24.5.0
      Boot Volume: Macintosh HD
      Boot Mode: Normal
      Computer Name: My MacBook Pro
      User Name: Alice Dev (alicedev)
      Secure Virtual Memory: Enabled
      System Integrity Protection: Enabled
      Time since boot: 5 days, 14 hours, 22 minutes

JSON output and jq piping

-json was added in macOS Monterey (12) and is the cleanest way to extract specific values in scripts. Pipe into jq for selection and transformation.

bash
system_profiler -json SPHardwareDataType

Output:

json
{
  "SPHardwareDataType" : [
    {
      "_name" : "hardware_overview",
      "activation_lock_status" : "activation_lock_enabled",
      "boot_rom_version" : "10151.121.1",
      "chip_type" : "Apple M2 Pro",
      "machine_model" : "Mac14,9",
      "machine_name" : "MacBook Pro",
      "model_number" : "MNW93LL/A",
      "number_processors" : "proc 10:6:4",
      "os_loader_version" : "10151.121.1",
      "physical_memory" : "16 GB",
      "platform_UUID" : "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
      "provisioning_UDID" : "XXXXXXXX-XXXXXXXXXXXXXXXX",
      "serial_number" : "XXXXXXXXXXX"
    }
  ]
}
bash
# Specific value — chip name
system_profiler -json SPHardwareDataType | jq -r '.SPHardwareDataType[0].chip_type'

Output:

text
Apple M2 Pro
bash
# Multiple values into a CSV row
system_profiler -json SPHardwareDataType SPSoftwareDataType | \
    jq -r '[
        .SPHardwareDataType[0].machine_name,
        .SPHardwareDataType[0].chip_type,
        .SPHardwareDataType[0].physical_memory,
        .SPHardwareDataType[0].serial_number,
        .SPSoftwareDataType[0].os_version
    ] | @csv'

Output:

text
"MacBook Pro","Apple M2 Pro","16 GB","XXXXXXXXXXX","macOS 15.5 (24F74)"
bash
# Total storage capacity in GB
system_profiler -json SPStorageDataType | \
    jq '[.SPStorageDataType[] | .size_in_bytes] | add / 1e9 | round'

Output:

text
494

XML output and plutil

Pre-Monterey systems and some data types still emit cleaner data via XML. The plutil tool converts between binary plist, XML plist, and JSON — perfect for older macOS targets or when you want to embed the dump inside an XML-aware system.

bash
system_profiler -xml SPHardwareDataType | head -25

Output:

text
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<dict>
		<key>_SPCommandLineArguments</key>
		<array>
			<string>/usr/sbin/system_profiler</string>
			<string>-nospawn</string>
			<string>-xml</string>
			<string>-detailLevel</string>
			<string>full</string>
			<string>SPHardwareDataType</string>
		</array>
		<key>_dataType</key>
		<string>SPHardwareDataType</string>
		<key>_items</key>
		<array>
			<dict>
				<key>_name</key>
				<string>hardware_overview</string>
			</dict>
		</array>
	</dict>
</array>
</plist>
bash
# Convert XML output to JSON for jq
system_profiler -xml SPHardwareDataType | plutil -convert json -o - - | \
    jq -r '.[0]._items[0].chip_type'

Output:

text
Apple M2 Pro
bash
# Extract one key by path with plutil directly (no jq required)
system_profiler -xml SPHardwareDataType | \
    plutil -extract "0._items.0.serial_number" raw -o - -

Output:

text
XXXXXXXXXXX

Detail levels

-detailLevel controls how much detail each probe collects. mini is the fastest but drops nested arrays (e.g. per-DIMM memory becomes just the total); full is the slowest and adds binary blobs you almost never need. The default depends on the data type — usually basic.

LevelBehaviourTypical use
miniSummarised, no nested arraysQuick scripts that only need top-level values
basicDefault for most data typesDay-to-day inspection
fullEverything, including binary blobs and full firmwareApple support diagnostics, deep audit
bash
# Compare detail levels for memory
system_profiler -detailLevel mini  SPMemoryDataType | wc -l
system_profiler -detailLevel basic SPMemoryDataType | wc -l
system_profiler -detailLevel full  SPMemoryDataType | wc -l

Output:

text
       6
      18
      72
bash
# Mini: just the total
system_profiler -detailLevel mini SPMemoryDataType

Output:

text
Memory:

      Memory: 16 GB
      Type: LPDDR5
      Manufacturer: Hynix

Common data types in depth

The next several sections cover the data types you'll reach for most often. Each section shows a typical text dump, then a jq recipe to pull a specific value out.

SPDisplaysDataType — graphics + monitors

SPDisplaysDataType reports the GPU (or, on Apple Silicon, the integrated graphics block) and every connected display with its native resolution, refresh rate, colour space, and connection type (built-in / HDMI / USB-C / AirPlay).

bash
system_profiler SPDisplaysDataType

Output:

text
Graphics/Displays:

    Apple M2 Pro:

      Chipset Model: Apple M2 Pro
      Type: GPU
      Bus: Built-In
      Total Number of Cores: 16
      Vendor: Apple (0x106b)
      Metal Support: Metal 3

      Displays:
        Built-in Liquid Retina XDR Display:
          Display Type: Built-in Liquid Retina XDR Display
          Resolution: 3024 x 1964 Retina
          Main Display: Yes
          Mirror: Off
          Online: Yes
          Connection Type: Internal
        DELL U2723QE:
          Resolution: 3840 x 2160 (2160p/4K UHD)
          UI Looks like: 1920 x 1080 @ 60.00Hz
          Mirror: Off
          Online: Yes
          Rotation: Supported
bash
# List every connected display with resolution
system_profiler -json SPDisplaysDataType | \
    jq -r '.SPDisplaysDataType[].spdisplays_ndrvs[]? |
           [._name, ._spdisplays_resolution // ._spdisplays_pixels] | @tsv'

Output:

text
Built-in Liquid Retina XDR Display	3024 x 1964 Retina
DELL U2723QE	3840 x 2160 (2160p/4K UHD)

SPMemoryDataType — RAM detail

On Intel Macs you get a per-DIMM breakdown (size, manufacturer, part number, ECC status). On Apple Silicon the RAM is on-package and reports as a single block.

bash
system_profiler SPMemoryDataType

Output (Apple Silicon):

text
Memory:

      Memory: 16 GB
      Type: LPDDR5
      Manufacturer: Hynix

Output (Intel):

text
Memory:

    Memory Slots:

      ECC: Enabled
      Upgradeable Memory: Yes

        BANK 0/DIMM0:
          Size: 8 GB
          Type: DDR4
          Speed: 2666 MHz
          Manufacturer: Samsung
          Part Number: M471A1K43DB1-CTD

        BANK 0/DIMM1:
          Size: 8 GB
          Type: DDR4
          ...

SPStorageDataType — every mounted volume

SPStorageDataType enumerates every Spotlight-indexable mount: the boot volume, every APFS sibling (Preboot, Recovery, Update, VM), external disks, USB sticks, and mounted DMGs.

bash
system_profiler SPStorageDataType

Output:

text
Storage:

    Macintosh HD:

      Free: 245.32 GB (245,324,576,768 bytes)
      Capacity: 494.38 GB (494,384,795,648 bytes)
      Mount Point: /
      File System: APFS
      Writable: Yes
      Ignore Ownership: No
      BSD Name: disk3s1s1
      Volume UUID: A8B4C5D6-E7F8-4901-A2B3-C4D5E6F70811
      Physical Drive:
          Device Name: APPLE SSD AP0512Z
          Media Name: APPLE SSD Controller
          Medium Type: SSD
          Protocol: Apple Fabric
          Internal: Yes
          Partition Map Type: GPT
          S.M.A.R.T. Status: Verified
bash
# Free space on every mounted volume
system_profiler -json SPStorageDataType | jq -r '
  .SPStorageDataType[] |
  [._name,
   .mount_point,
   (.free_space_in_bytes / 1e9 | floor | tostring + " GB"),
   (.size_in_bytes       / 1e9 | floor | tostring + " GB")
  ] | @tsv'

Output:

text
Macintosh HD	/	245 GB	494 GB
Backup	/Volumes/Backup	1842 GB	2000 GB
USB	/Volumes/USB	28 GB	128 GB

SPUSBDataType — USB topology

The USB tree is reported hierarchically: each root hub (built-in USB controller), then any external hubs, then leaf devices (keyboards, drives, cameras). Useful for diagnosing dead ports or finding the VID/PID of a misbehaving peripheral.

bash
system_profiler SPUSBDataType

Output (excerpt):

text
USB:

    USB 3.1 Bus:

      Host Controller Driver: AppleEmbeddedUSBXHCIFL1100
      PCI Device ID: 0x1100
      PCI Revision ID: 0x0010
      PCI Vendor ID: 0x1b73

        Apple T2 Bus:
          Apple T2 Controller:

            Bluetooth USB Host Controller:
              Product ID: 0x8290
              Vendor ID: 0x05ac  (Apple Inc.)

            FaceTime HD Camera (Built-in):
              Product ID: 0x8514
              Vendor ID: 0x05ac  (Apple Inc.)

        Logitech USB Receiver:
          Product ID: 0xc52b
          Vendor ID: 0x046d  (Logitech Inc.)
bash
# Flat list of every USB device with vendor/product IDs
system_profiler -json SPUSBDataType | \
    jq -r '[ .. | select(type=="object" and has("product_id"))? |
             [._name, .vendor_id, .product_id] ] | .[] | @tsv'

Output:

text
Bluetooth USB Host Controller	0x05ac  (Apple Inc.)	0x8290
FaceTime HD Camera (Built-in)	0x05ac  (Apple Inc.)	0x8514
Logitech USB Receiver	0x046d  (Logitech Inc.)	0xc52b
DELL U2723QE	0x413c  (Dell Inc.)	0xb09e

SPNetworkDataType — interfaces and routes

SPNetworkDataType reports every network service with its order, type, IP addresses, gateway, DNS, and DHCP state.

bash
system_profiler SPNetworkDataType

Output (excerpt):

text
Network:

    Wi-Fi:

      Type: AirPort
      Hardware: AirPort
      BSD Device Name: en0
      IPv4:
          Addresses: 192.168.1.42
          Configuration Method: DHCP
          Interface Name: en0
          Router: 192.168.1.1
          Subnet Masks: 255.255.255.0
      DNS:
          Server Addresses: 1.1.1.1, 8.8.8.8
      DHCP Server Responses:
          Lease Duration (seconds): 86400
          Routers: 192.168.1.1
          Server Identifier: 192.168.1.1
bash
# Just the primary IPv4 of the first up interface
system_profiler -json SPNetworkDataType | jq -r '
  .SPNetworkDataType[] |
  select(.ip_address) |
  [._name, .ip_address[0]] | @tsv' | head -1

Output:

text
Wi-Fi	192.168.1.42

SPApplicationsDataType — installed apps

SPApplicationsDataType lists every .app bundle on the system with its version, path, signature ("Apple", "Identified Developer", "None"), and Last-Modified date. Slow to enumerate (~5–30 seconds) on a typical Mac with hundreds of apps.

bash
system_profiler SPApplicationsDataType | head -20

Output:

text
Applications:

    1Password:

      Version: 8.10.40
      Obtained from: Identified Developer
      Last Modified: 5/22/2026, 14:23
      Kind: Apple Silicon
      Signed by: Developer ID Application: AgileBits Inc. (2BUA8C4S2C)
      Location: /Applications/1Password.app

    Activity Monitor:

      Version: 10.15
      Obtained from: Apple
      Last Modified: 5/15/2026, 09:00
      Kind: Apple Silicon
      Signed by: Software Signing
bash
# Every app from "Identified Developer" (signed but not from Apple)
system_profiler -json SPApplicationsDataType | jq -r '
  .SPApplicationsDataType[] |
  select(.obtained_from == "identified_developer") |
  [._name, .version, .path] | @tsv'

Output:

text
1Password	8.10.40	/Applications/1Password.app
Visual Studio Code	1.91.0	/Applications/Visual Studio Code.app
Firefox	126.0.1	/Applications/Firefox.app
Slack	4.39.0	/Applications/Slack.app

SPPowerDataType — battery + sleep

SPPowerDataType reports battery health, cycle count, current charge, AC adapter wattage, and the full sleep/wake configuration. The same info is available piecemeal from pmset -g batt and ioreg, but SPPowerDataType is the easy structured form.

bash
system_profiler SPPowerDataType | sed -n '1,40p'

Output:

text
Power:

    Battery Information:

      Model Information:
          Manufacturer: SMP
          Device Name: bq40z651
          Pack Lot Code: 0
          PCB Lot Code: 0
          Firmware Version: 1
          Hardware Revision: 1
          Cell Revision: 1543
      Charge Information:
          The battery's charge is full: No
          Fully charged: No
          Charging: Yes
          State of Charge (%): 78
      Health Information:
          Cycle Count: 142
          Condition: Normal
          Maximum Capacity: 96%
      AC Charger Information:
          Connected: Yes
          ID: 0x0000
          Charging: Yes
          Wattage (W): 96
          Family: 0xe000400a
          Serial Number: F1H2J3K4L5
          Name: USB-C Power Adapter
bash
# Cycle count and condition as a one-liner
system_profiler -json SPPowerDataType | jq -r '
  .SPPowerDataType[]
    | select(.sppower_battery_health_info)
    | "Cycles: \(.sppower_battery_health_info.sppower_battery_cycle_count)\nHealth: \(.sppower_battery_health_info.sppower_battery_health)"'

Output:

text
Cycles: 142
Health: Normal

Combining multiple data types

You can pass any number of data type arguments and system_profiler returns them in one document. JSON output makes a single jq query trivial.

bash
system_profiler -json SPHardwareDataType SPSoftwareDataType SPStorageDataType | \
    jq '{
        host:       .SPSoftwareDataType[0].local_host_name,
        model:      .SPHardwareDataType[0].machine_name,
        chip:       .SPHardwareDataType[0].chip_type,
        ram:        .SPHardwareDataType[0].physical_memory,
        serial:     .SPHardwareDataType[0].serial_number,
        os:         .SPSoftwareDataType[0].os_version,
        boot_vol:   .SPSoftwareDataType[0].boot_volume,
        total_gb:   ([.SPStorageDataType[].size_in_bytes] | add / 1e9 | round),
        free_gb:    ([.SPStorageDataType[].free_space_in_bytes] | add / 1e9 | round)
    }'

Output:

json
{
  "host": "myhost",
  "model": "MacBook Pro",
  "chip": "Apple M2 Pro",
  "ram": "16 GB",
  "serial": "XXXXXXXXXXX",
  "os": "macOS 15.5 (24F74)",
  "boot_vol": "Macintosh HD",
  "total_gb": 2622,
  "free_gb": 2115
}

Performance and timeouts

A full default system_profiler (every data type) takes 30–60 seconds on a modern Mac and can hit 90+ seconds if a misbehaving USB device blocks the USB probe. Two strategies tame this: limit to the data types you actually need, and set -timeout to cap each probe.

bash
# Time a focused query
time system_profiler -json SPHardwareDataType SPSoftwareDataType > /dev/null

Output:

text
system_profiler -json SPHardwareDataType SPSoftwareDataType > /dev/null
        0.32 real         0.06 user         0.02 sys
bash
# Vs the full kitchen sink
time system_profiler -json > /dev/null

Output:

text
system_profiler -json > /dev/null
       42.18 real         8.41 user         4.32 sys
bash
# Hard cap a slow probe (e.g. SPUSBDataType if a hub is misbehaving)
system_profiler -timeout 5 SPUSBDataType > /tmp/usb.txt 2>&1
echo "exit=$?"

Output:

text
exit=0

Comparison vs sysctl, ioreg, sw_vers

system_profiler is the inventory tool. For tight one-value lookups in scripts, three peers are faster:

NeedBest toolExample
One kernel/hw value (RAM, CPU count)sysctlsysctl -n hw.memsize
Live IORegistry (hardware, USB, sensors)ioregioreg -p IOUSB
macOS version + buildsw_verssw_vers -productVersion
Full structured inventorysystem_profilersystem_profiler -json SPHardwareDataType
Pending updatessoftwareupdatesoftwareupdate -l
bash
# Three ways to get the chip name
system_profiler -json SPHardwareDataType | jq -r '.SPHardwareDataType[0].chip_type'
sysctl -n machdep.cpu.brand_string
ioreg -arc IOPlatformDevice -k product-name 2>/dev/null | \
    plutil -extract '0."product-name"' raw -o - -

Output:

text
Apple M2 Pro
Apple M2 Pro
Apple M2 Pro

For a single field, sysctl is ~100× faster (sub-millisecond vs ~300 ms). For ten different fields with cross-cutting structure, system_profiler -json is more convenient.

Common pitfalls

  1. JSON output requires macOS 12+ — on older Macs use -xml plus plutil -convert json -o - -.
  2. Whitespace inside -listDataTypes output — the leading indentation differs slightly between macOS versions; trim before piping.
  3. Some data types need rootSPSmartCardsDataType, SPDiagnosticsDataType, SPInstallHistoryDataType (partial), and SPLogsDataType return less info without sudo. Most hardware data types do not need root.
  4. SPApplicationsDataType is expensive — it scans every /Applications, ~/Applications, and /System/Applications .app bundle and reads its Info.plist. 5–30 seconds typical. Cache the output for scripts that re-query.
  5. JSON field names use snake_case, text dumps use Title Case — there is no automatic mapping. To find the JSON key for "Total Number of Cores", grep the JSON for the value: system_profiler -json SPHardwareDataType | jq '..|select(type=="string")?|select(contains("10:6:4"))?' shows the surrounding key.
  6. Two arrays for displaysSPDisplaysDataType returns one item per GPU; each item has spdisplays_ndrvs containing the connected monitors. Forgetting this nesting gives you the GPU name when you wanted the monitor name.
  7. Activation Lock and Serial Number leak PII — when sharing output publicly, redact serial_number, provisioning_UDID, and platform_UUID.
  8. SPUSBDataType deep nesting — devices behind hubs appear as _items inside their parent's _items. Use .. | objects | select(has("product_id")) in jq to flatten.
  9. -detailLevel full enables more PII — full detail dumps FileVault recovery key SHAs, BootROM personalization, and other forensic data. Use mini or basic for inventory exports.
  10. Timezone-sensitive date fieldslast_modified in SPApplicationsDataType is reported in local time without a tz marker. Convert with date -j -f before storing in a UTC database.
  11. No --help flag — use system_profiler -usage or system_profiler DATATYPE -usage for per-type detail-level help.
  12. Caching the wrong fields — Apple periodically renames JSON keys between macOS major versions (chip_type was cpu_type on Catalina). Pin keys per macOS version when writing long-lived inventory tooling.

Real-world recipes

One-line "what is this Mac?"

The fast inventory line you can SSH into any Mac to grab.

bash
system_profiler -json SPHardwareDataType SPSoftwareDataType | jq -r '
    "\(.SPSoftwareDataType[0].local_host_name) | \(.SPHardwareDataType[0].machine_name) | \(.SPHardwareDataType[0].chip_type) | \(.SPHardwareDataType[0].physical_memory) | \(.SPSoftwareDataType[0].os_version)"'

Output:

text
myhost | MacBook Pro | Apple M2 Pro | 16 GB | macOS 15.5 (24F74)

Fleet JSON inventory

A canonical snapshot per host, suitable for daily ingestion into an asset DB.

bash
#!/bin/bash
# inventory.sh — emit JSON inventory of this Mac to stdout
set -euo pipefail

system_profiler -json \
    SPHardwareDataType \
    SPSoftwareDataType \
    SPStorageDataType \
    SPMemoryDataType \
    SPPowerDataType \
    SPNetworkDataType | \
jq '{
    host:          .SPSoftwareDataType[0].local_host_name,
    timestamp:     now | strftime("%Y-%m-%dT%H:%M:%SZ"),
    model:         .SPHardwareDataType[0].machine_name,
    model_id:      .SPHardwareDataType[0].machine_model,
    chip:          .SPHardwareDataType[0].chip_type,
    cores:         .SPHardwareDataType[0].number_processors,
    serial:        .SPHardwareDataType[0].serial_number,
    ram_gb:        (.SPHardwareDataType[0].physical_memory | gsub(" GB";"") | tonumber),
    ram_type:      .SPMemoryDataType[0].dimm_type,
    os:            .SPSoftwareDataType[0].os_version,
    kernel:        .SPSoftwareDataType[0].kernel_version,
    sip:           .SPSoftwareDataType[0].system_integrity,
    boot_mode:     .SPSoftwareDataType[0].boot_mode,
    uptime:        .SPSoftwareDataType[0].uptime,
    storage:       [.SPStorageDataType[] | {
        name:       ._name,
        mount:      .mount_point,
        fs:         .file_system,
        size_gb:    (.size_in_bytes / 1e9 | round),
        free_gb:    (.free_space_in_bytes / 1e9 | round)
    }],
    battery:       (.SPPowerDataType[]?
                       | select(.sppower_battery_health_info)
                       | {
                           cycles:  .sppower_battery_health_info.sppower_battery_cycle_count,
                           health:  .sppower_battery_health_info.sppower_battery_health,
                           max_pct: .sppower_battery_health_info.sppower_battery_health_maximum_capacity
                       }),
    primary_ip:    (.SPNetworkDataType[]? | select(.ip_address) | .ip_address[0])
}'

Output: (saved as inventory.sh — run it below)

bash
./inventory.sh

Output:

json
{
  "host": "myhost",
  "timestamp": "2026-05-25T10:30:15Z",
  "model": "MacBook Pro",
  "model_id": "Mac14,9",
  "chip": "Apple M2 Pro",
  "cores": "proc 10:6:4",
  "serial": "XXXXXXXXXXX",
  "ram_gb": 16,
  "ram_type": "LPDDR5",
  "os": "macOS 15.5 (24F74)",
  "kernel": "Darwin 24.5.0",
  "sip": "integrity_enabled",
  "boot_mode": "boot_mode_normal",
  "uptime": "up 5 days, 14:22",
  "storage": [
    { "name": "Macintosh HD", "mount": "/", "fs": "APFS", "size_gb": 494, "free_gb": 245 }
  ],
  "battery": { "cycles": 142, "health": "Normal", "max_pct": "96%" },
  "primary_ip": "192.168.1.42"
}

Inventory across SSH'd fleet

A loop that collects the snapshot from each host into a single line-delimited JSON file.

bash
#!/bin/bash
> fleet-inventory.jsonl
for host in mac01 mac02 mac03 mac04 mac05; do
    echo "Probing $host..."
    ssh "$host" 'cat /usr/local/bin/inventory.sh | bash' >> fleet-inventory.jsonl
done
echo "Hosts: $(wc -l < fleet-inventory.jsonl)"

Output:

text
Probing mac01...
Probing mac02...
Probing mac03...
Probing mac04...
Probing mac05...
Hosts: 5
bash
# Query the inventory with jq
jq -r '. | [.host, .model, .chip, .ram_gb, .battery.cycles] | @tsv' fleet-inventory.jsonl

Output:

text
mac01	MacBook Pro	Apple M2 Pro	16	142
mac02	MacBook Air	Apple M2	8	57
mac03	Mac mini	Apple M2 Pro	32	0
mac04	iMac	Apple M1	16	0
mac05	MacBook Pro	Apple M3 Max	64	23

Find every Intel-only app on this Mac

Apple Silicon migration audit: which .apps are still x86_64-only (will require Rosetta)?

bash
system_profiler -json SPApplicationsDataType | jq -r '
  .SPApplicationsDataType[] |
  select(.arch_kind == "arch_i64") |
  [._name, .version, .path] | @tsv'

Output:

text
LegacyTool	2.1.0	/Applications/LegacyTool.app
OldKitApp	5.7.3	/Applications/OldKitApp.app

Find the largest .app bundles on disk

Storage-cleanup tool: rank apps by disk footprint.

bash
system_profiler -json SPApplicationsDataType | jq -r '
  .SPApplicationsDataType[] | [._name, .path] | @tsv' | \
while IFS=$'\t' read -r name path; do
    size=$(du -sh "$path" 2>/dev/null | cut -f1)
    printf "%-10s %s\n" "$size" "$name"
done | sort -rh | head -10

Output:

text
8.4G       Xcode
4.1G       Affinity Photo
2.8G       Microsoft Word
2.6G       Visual Studio Code
1.9G       Final Cut Pro

Detect a stale BootROM

For organisations that need every Mac on a known-good BootROM firmware version.

bash
EXPECTED="10151.121.1"
ACTUAL=$(system_profiler -json SPHardwareDataType | jq -r '.SPHardwareDataType[0].boot_rom_version')
if [[ "$ACTUAL" == "$EXPECTED" ]]; then
    echo "BootROM OK ($ACTUAL)"
else
    echo "BootROM drift: have=$ACTUAL want=$EXPECTED"
fi

Output:

text
BootROM OK (10151.121.1)

Diagnose a flaky USB hub

When lsusb would say "device is glitching" on Linux, here's the Mac equivalent.

bash
# Snapshot 1
system_profiler -json SPUSBDataType > /tmp/usb-before.json
# Trigger / wait / re-snapshot
sleep 30
system_profiler -json SPUSBDataType > /tmp/usb-after.json

# Diff just the leaf device names
diff <(jq -r '..|objects|select(has("product_id"))?._name' /tmp/usb-before.json | sort) \
     <(jq -r '..|objects|select(has("product_id"))?._name' /tmp/usb-after.json  | sort)

Output:

text
3d2
< Logitech USB Receiver

(A USB device disappeared between snapshots — physical port flake or driver issue.)

Export a PDF for a support ticket

Apple Support frequently asks for a full System Information report. The GUI lets you "File → Export → Send to Apple"; the CLI equivalent is plain text into a PDF via cupsfilter or enscript.

bash
system_profiler -detailLevel full > ~/Desktop/sysinfo-$(date +%Y%m%d).txt
# Optional: convert to PDF
textutil -convert webarchive -inputencoding UTF-8 ~/Desktop/sysinfo-*.txt -output ~/Desktop/sysinfo.webarchive

Output: (none — files written)

Spot kernel extensions installed by third parties

Kernel extensions are deprecated but still present on older deployments. List anything not signed by Apple.

bash
system_profiler -json SPExtensionsDataType | jq -r '
    .SPExtensionsDataType[]
    | select(.signed_by != "Software Signing" and .signed_by != null)
    | [.spext_bundle_id, .spext_version, .signed_by] | @tsv' | head -20

Output:

text
com.crowdstrike.kext	7.20.16404	Developer ID Application: CrowdStrike, Inc.
com.parallels.kext.hypervisor	19.4.0	Developer ID Application: Parallels International GmbH

For a continuously-updated inventory dashboard, run the JSON inventory script via a launchd StartCalendarInterval agent and have it POST to your central inventory API. See launchctl for the plist pattern.

[!WARN] Never ship raw system_profiler output to a public bug tracker. It contains serial_number, platform_UUID, IP addresses, MAC addresses, paired Bluetooth device names, and Wi-Fi network names. Redact before sharing.

Sources

References consulted while writing this article. Links open in a new tab.

  • Apple Developer — system_profiler(8) man page — Authoritative flag list, data-type names, and detail levels used while writing the options reference.
  • SS64 — system_profiler — Cross-version notes.

See also

  • macos-cli — broader macOS terminal reference (sw_vers, sysctl, ioreg).
  • softwareupdate — patch-level inventory and update install.
  • jq — JSON query language used throughout the recipes here.
  • systeminfo — Windows analogue for fleet-level OS/hardware inventory.