47

What command can I use to retrieve the system's serial number from the unix command line? As uname will output some information about the software and hardware, I would like to retrieve the serial number from a command to use in a script.

bmike
  • 235,889

7 Answers7

67

The system_profiler command provides a direct answer that’s easily human readable (assuming you are on 10.3 or newer), but you can also use ioreg for the task as it generally completes faster.

system_profiler SPHardwareDataType is the data type that contains the core hardware information, and you can use grep or awk to pare things down further as needed:

system_profiler SPHardwareDataType | awk '/Serial/ {print $4}'

or

ioreg -l | awk '/IOPlatformSerialNumber/ { print $4;}'

Both of those commands take between 0.5 and 0.2 seconds to run on modern SSD Macs, so if you want to optimize the command and remove the " you can have your answer in 0.005s or so:

ioreg -c IOPlatformExpertDevice -d 2 | awk -F\" '/IOPlatformSerialNumber/{print $(NF-1)}'
bmike
  • 235,889
18

This also works…

ioreg -l | grep IOPlatformSerialNumber
grg
  • 201,078
MrPool
  • 191
4

C++ example from https://developer.apple.com/library/archive/technotes/tn1103/_index.html:

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>

std::string

getSerialNumber()

{

CFStringRef serial; char buffer[32] = {0}; std::string seriaNumber;

io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); if (platformExpert) { CFTypeRef serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0); if (serialNumberAsCFString) { serial = (CFStringRef)serialNumberAsCFString; } if (CFStringGetCString(serial, buffer, 32, kCFStringEncodingUTF8)) { seriaNumber = buffer; }

IOObjectRelease(platformExpert);

} return seriaNumber; }

nohillside
  • 100,768
2

This works in Python 3.10.5 on macOS

import json
import os
import time

Example MySPHardwareDataType.json created and used by this script

#{

"SPHardwareDataType" : [

{

"_name" : "hardware_overview",

"activation_lock_status" : "activation_lock_disabled",

"boot_rom_version" : "7459.121.3",

"chip_type" : "Apple M1",

"machine_model" : "MacBookPro17,1",

"machine_name" : "MacBook Pro",

"number_processors" : "proc 8:4:4",

"os_loader_version" : "7459.121.3",

"physical_memory" : "16 GB",

"platform_UUID" : "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",

"provisioning_UDID" : "XXXXXXXX-XXXXXXXXXXXXXXXX",

"serial_number" : "XXXXXXXXXXXX"

}

]

#}

def export_basic_hwinfo_to_json(json_filename_and_path):

# This is the command that will execute.  It will run macOS' built-in
# system_profiler binary, filtered to only show the contents of 
# SPHardwareDataType, which contains the data we are looking for.
# Additionally, we have also specified that it should be exported to JSON
# format, and that standard output should be redirected to the file specified earlier
command = &quot;system_profiler SPHardwareDataType -json &gt;&quot; + json_filename_and_path

# execute the command
os.popen(command)

# This loop waits for the file to exist before continuing
while os.path.exists(json_filename_and_path) is False:
    time.sleep(1)


def get_serial(json_filename_and_path) -> str: # open the .json file for processing file_handle = open(json_filename_and_path)

# load the contents of the .json file into a Python dictionary
hw_info_dict = json.load(file_handle)

# retrieve the data we are looking for
serial = hw_info_dict['SPHardwareDataType'][0]['serial_number']
return serial


def get_machine_sku(json_filename_and_path) -> str: # open the .json file for processing file_handle = open(json_filename_and_path)

# load the contents of the .json file into a Python dictionary
hw_info_dict = json.load(file_handle)

# retrieve the data we are looking for
machine_sku = hw_info_dict['SPHardwareDataType'][0]['machine_model']
return machine_sku


def get_model(json_filename_and_path) -> str: # open the .json file for processing file_handle = open(json_filename_and_path)

# load the contents of the .json file into a Python dictionary
hw_info_dict = json.load(file_handle)

# retrieve the data we are looking for
model = hw_info_dict['SPHardwareDataType'][0]['machine_name']
return model


def main():

# We are going to store the output file in a temp directory
temp_dir: str = os.environ.get('TMPDIR')
temp_dir: str = temp_dir[:-1]

json_filename: str = &quot;MySPHardwareDataType.json&quot;

# Cleanly append the name of the output file to the file path
json_filename_and_path: str = os.sep.join([temp_dir, json_filename])

export_basic_hwinfo_to_json(json_filename_and_path)

serial: str = get_serial(json_filename_and_path)
machine_sku: str = get_machine_sku(json_filename_and_path)
model: str = get_model(json_filename_and_path)

print(F&quot;serial={serial}&quot;)
print(F&quot;machine_sku={machine_sku}&quot;)
print(F&quot;model={model}&quot;)

if name == "main": main()

1

To make it easier to remember, use a case insensitive search:

ioreg -l | grep -i serialnumber | grep -i platform
Glorfindel
  • 4,057
Jared
  • 19
  • 1
  • Warning: this returns a line | "IOPlatformSerialNumber" = "YourSerialNumber" That’s fine visually but if you use it in a script you'll need to pipe it to awk. – jasonology Jun 25 '19 at 04:28
0

If you don't want to count columns you can rely on the fact that the value is always in the last one and use

system_profiler SPHardwareDataType | awk '/Serial/{print $NF}'
nohillside
  • 100,768
  • gee, thanks mom @nohillside; we wouldn't want to subject people to anything that isn't kid gloves and kudos, you know, like reality. – christian elsee Jul 04 '21 at 00:56
  • SE works a bit different than forum sites. The way to give kudos is to upvote posts you like or which helped you. – nohillside Jul 04 '21 at 06:18
  • I wasnt giving kudos @nohillside, I was criticizing the members of the thread that managed to waste time and misinform by discussing benchmarks around ioreg and system_profiler, giving weight to something that NO ONE outside of exceptional circumstances will ever concern themselves with. My beef, is that one day a junior ops engineer, will point to this or something like it, when defending a pull request that "optimizes" the serial-number-as-a-rube-goldberg-service, which is famously held together with spit and tape. Maybe balancing the carrot with the stick will help lead to less of that. – christian elsee Jul 04 '21 at 23:29
  • Please have a look at https://apple.stackexchange.com/help/how-to-answer for what we look for in answers. Also, this is a Q&A site not a forum, answers should stand on their own and not discuss other answers. If you want to discuss or criticize other answers, please use comments beneath that answer or the chat. – nohillside Jul 05 '21 at 04:12
0

It's potentially slightly cleaner to use the XML output of ioreg rather than parsing the human-readable form with sed/awk:

ioreg -c IOPlatformExpertDevice -d 2 -a | xmllint --xpath "/plist/dict[key='IORegistryEntryChildren']/array/dict/key[.='IOPlatformSerialNumber']/following-sibling::string[1]/node()" -
aglet
  • 1