On a Mac you have only shell access to, how do you determine the macOS version and whether the hardware is Apple Silicon or Intel — and what can make each of those checks report the wrong thing?
answer
- three separate tools, three separate facts
- product version is not the kernel release
- architecture is per-process, not per-machine
- translation makes uname report x86_64
- sysctl.proc_translated returns 1
basics
~20 sUse sw_vers for the macOS product version and build, uname -m for the CPU architecture (arm64 on Apple Silicon, x86_64 on Intel), and system_profiler SPHardwareDataType for model, chip and serial number. Rosetta translation can make uname -m report x86_64 on an Apple Silicon machine.
solid answer
~40 sThree tools cover it. `sw_vers` prints `ProductName`, `ProductVersion` and `BuildVersion`, and `sw_vers -productVersion` is the scriptable form. `uname -m` prints the machine architecture — `arm64` on Apple Silicon, `x86_64` on Intel. `system_profiler SPHardwareDataType` gives the inventory-grade facts: model name, model identifier, chip, serial number and hardware UUID, and it supports `-json` for parsing. The traps are worth naming: `uname -m` reports the architecture of the *calling process*, so a shell translated by Rosetta 2 on an Apple Silicon Mac says `x86_64` — check `sysctl -n sysctl.proc_translated`, which is `1` when the process is translated. And `sw_vers` can be forced into legacy mode by the `SYSTEM_VERSION_COMPAT` environment variable, which makes macOS 11 and later report `10.16`. `uname -r` gives the Darwin kernel release, not the macOS version.
code
bash · 15 lines#!/bin/bash
os_version=$(sw_vers -productVersion)
os_build=$(sw_vers -buildVersion)
arch=$(uname -m)
translated=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
if [ "$translated" = "1" ]; then
arch="arm64 (shell running translated as x86_64)"
fi
model=$(sysctl -n hw.model)
chip=$(sysctl -n machdep.cpu.brand_string)
printf 'macOS %s (%s)\narch: %s\nmodel: %s\nchip: %s\n' \
"$os_version" "$os_build" "$arch" "$model" "$chip"go deeper
Know the three commands by name — sw_vers, uname -m, system_profiler — and say plainly that uname -r gives the Darwin kernel release, not the macOS version.
Explain that uname -m describes the calling process, so a Rosetta-translated shell reports x86_64, and name sysctl.proc_translated as the check that settles it.
Show how you would build this into fleet inventory: named system_profiler data types with -json, cheap sysctl reads inside loops, and guarding against an inherited SYSTEM_VERSION_COMPAT skewing version comparisons.
Own the policy question: what identity fields your fleet keys on (serial, hardware UUID, build version), how those flow into asset and compliance systems, and why coarse product-version gating causes rollout incidents.
## Why the question is asked Every Mac fleet script starts with the same two branches: which macOS is this, and which silicon is this. Getting either wrong sends a machine down the wrong install path, so an interviewer wants to hear both the command and the failure mode. ## The macOS version `sw_vers` reads the system version property list and prints three fields: ```bash $ sw_vers ProductName: macOS ProductVersion: 15.5 BuildVersion: 24F74 ``` Each field has a scriptable flag — `sw_vers -productVersion`, `sw_vers -buildVersion`, `sw_vers -productName` — which is what you want in automation so you are not parsing tab-separated output. Two distinctions matter. First, the **build version** is the precise identifier: two Macs can both say `15.5` while running different builds, and Apple ships rapid security responses and hardware-specific builds, so support tooling logs the build. Second, `uname -r` is *not* the macOS version — it is the Darwin kernel release, which advances on its own numbering. Answering `uname -r` to a macOS-version question is a common miss. The surprising failure is `SYSTEM_VERSION_COMPAT`. Apple kept a compatibility shim for software that assumed the version would always begin with `10.`: when that environment variable is set (or when a binary was linked against an old SDK), `sw_vers` and the corresponding APIs report `10.16` instead of the real major version. If a script inherits that variable, every version comparison it makes is wrong. ## The hardware `uname -m` prints the machine hardware name: `arm64` on Apple Silicon, `x86_64` on Intel. The catch is that this describes the process asking, not the machine. Rosetta 2 translates x86_64 binaries on Apple Silicon, and a translated shell — one launched from a terminal with the "Open using Rosetta" option, or invoked through `arch -x86_64` — sees `x86_64`. The authoritative check is: ```bash if [ "$(sysctl -n sysctl.proc_translated 2>/dev/null)" = "1" ]; then echo "running under Rosetta translation" fi ``` That sysctl exists only on Apple Silicon and returns `1` for a translated process, `0` otherwise. This matters in practice because a translated shell also inherits an x86_64 view of the world — it will happily install the wrong architecture of a tool and leave you debugging "works on my machine" for an afternoon. For everything else, `system_profiler` is the inventory tool. It fronts the same data the System Information app shows, organised into data types: ```bash $ system_profiler SPHardwareDataType Hardware Overview: Model Name: MacBook Pro Model Identifier: MacBookPro18,3 Chip: Apple M1 Pro Serial Number (system): XXXXXXXXXX Hardware UUID: ... ``` Other useful types are `SPSoftwareDataType` (OS version, boot volume, uptime), `SPStorageDataType` and `SPNetworkDataType`. `system_profiler -json SPHardwareDataType` emits JSON, which is the right form for a fleet-reporting script. The trade-off is speed: `system_profiler` with no arguments walks every data type and takes many seconds, so always name the type you want. Lighter-weight equivalents exist through `sysctl`: `sysctl -n hw.model` gives the model identifier (for example `MacBookPro18,3`), and `sysctl -n machdep.cpu.brand_string` gives the CPU or chip description. These return in milliseconds and are what you reach for inside a loop. ## Putting it together A defensible inventory snippet reads the version from `sw_vers`, the architecture from `uname -m` guarded by the translation check, and the identity fields from `system_profiler`. What separates a good answer from a memorised one is naming *why* each check can lie: a translated process, an inherited compatibility variable, and the kernel release being a different number from the product version.
- Your inventory script reports x86_64 for a Mac you know has an M-series chip. Walk me through diagnosing it.Almost certainly the script ran in a translated process. Check `sysctl -n sysctl.proc_translated` — a `1` means Rosetta 2 is translating the shell, so `uname -m` describes the process, not the hardware. Confirm against `system_profiler SPHardwareDataType` or `sysctl -n hw.model`, which report the real machine. Then fix the caller: an agent or terminal launched under Rosetta, or an explicit `arch -x86_64` wrapper.
- Why would you log the build version rather than just the product version?The product version is coarse. Apple ships different builds of the same version for particular hardware, plus rapid security responses that change behaviour without moving the product version. When you are correlating a bug across a fleet, the build string from `sw_vers -buildVersion` is the identifier that actually distinguishes machines, and it is what Apple's own support flows ask for.
saying these in an interview costs you the question
- Answering uname -r for the macOS version
- Trusting uname -m without considering Rosetta translation
- Running bare system_profiler in a loop and blaming it for being slow
- Parsing sw_vers output instead of using -productVersion
- Assuming the model name and model identifier are the same string