Script to extract all applications installed on a Mac

Recently, I needed to extract a full list of programs installed on a couple of Macs.  What I wanted to do was get an inventory of all of the software installed on each one and then load it into Excel and identify differences.

I initially assumed that would be simple, and I would just use a shell script to look in a couple of directories and compile a list. Unfortunately, it turns out not to be so simple, with applications scattered across a wide range of locations.

I spent a little while looking for programs that could export a list, but none of them quite fit my needs and price range. They tended to be expensive auditing tools that cost more than I wanted to spend.

After poking around for a while further, I found that Apple does have a built-in tool to audit what is on a Mac.  If you select “About This Mac” from the Apple menu and choose “More Info…”, there is an option to pull a “System Report”, which opens a complex screen with detailed information about the computer. It contains hardware information, network data, and an extensive report of all the installed software and packages.

The system report had all the information I wanted, but I needed to extract the data

The system report had all the information I wanted, but I needed to extract the data

Everything I needed was here, but I needed some way to load it into Excel so that I could compare a couple of machines at once.  I did manage to find a “Save…” option in the menu bar, and this had me save a “.spx” file.  I’m assuming that this file allows users to send a report to Apple tech support (or other support organizations) with detailed information about the computer.

I tried opening up the file in a text editor, and thankfully the data was in text form, so I knew I would be able to extract it. Unfortunately, the data was in XML. In order to get it into a format that I could analyze in Excel, I would have some work to do.

XML is a very flexible format, but unfortunately the main tool for transforming it is just awful – XSLT (Extensible Stylesheet Language Transformations).  It’s kind of like a programming language, but not really. Every time I have had to use it, I have felt like I was wading through a pit of mud.

Still, I was determined to have a go at it. As I looked at the format, I saw that Apple wasn’t exactly making it easy. Rather than using attributes and node types, the data was encoded into alternating name/value pairs, where each <key> tag told you the meaning of the data in the tag that comes after:

<string>Google Chrome</string>
<string>Developer ID Application: Google Inc.</string>
<string>Developer ID Certification Authority</string>
<string>Apple Root CA</string>

What a pain! Instead of being able to look for specific node / attribute name combinations, you needed to focus on the order of the data. XSLT doesn’t have variables in the normal sense of the word (XSLT variables can only be set once!), so logic where you need to keep track of the previous/next node values is tricky. As I did some research, I found that the XSLT actually provides a special “following-sibling” reference that allows you to automatically grab values from the node after the current one, which ended up making the task quite simple.

My XSLT looks at two specific sections and then outputs tab-delimited values for certain keys.  Specifically, it grabs each of the sections and then outputs the data for the keys below:

  • Applications (SPApplicationsDataType)
    • _name
    • obtained_from
    • version
    • info
    • path
  • Installations (SPInstallHistoryDataType)
    • _name
    • package_source
    • install_version

I’m sharing it here in case anyone else needs to do something similar, in which case they can work from my script.

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"

<xsl:output method="text" indent="yes" encoding="utf-8" standalone="yes"/>

<xsl:template match="plist/array/dict[string='SPApplicationsDataType']/array/dict">
 <xsl:value-of select="normalize-space(key[text() = '_name']/following-sibling::string)"/>
 <xsl:text>	</xsl:text>
 <xsl:value-of select="normalize-space(key[text() = 'obtained_from']/following-sibling::string)"/>
 <xsl:text>	</xsl:text>
 <xsl:value-of select="normalize-space(key[text() = 'version']/following-sibling::string)"/>
 <xsl:text>	</xsl:text>
 <xsl:value-of select="normalize-space(key[text() = 'info']/following-sibling::string)"/>
 <xsl:text>	</xsl:text>
 <xsl:value-of select="normalize-space(key[text() = 'path']/following-sibling::string)"/>

<xsl:template match="plist/array/dict[string='SPInstallHistoryDataType']/array/dict">
 <xsl:value-of select="normalize-space(key[text() = '_name']/following-sibling::string)"/>
 <xsl:text>	</xsl:text>
 <xsl:value-of select="normalize-space(key[text() = 'package_source']/following-sibling::string)"/>
 <xsl:text>	</xsl:text>
 <xsl:value-of select="normalize-space(key[text() = 'install_version']/following-sibling::string)"/>
 <xsl:text>	</xsl:text>
 <xsl:text>	</xsl:text>


<xsl:template match="text()">

<xsl:template match="/">

To use it, save the code above into an xslt file (e.g. application_extract.xslt).  Then, use the System Report to save a .xsd file.  Finally, run the following command:

xsltproc application_extract.xslt computer_info.xsd > tab_delimited_results.txt


This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

3 Responses to Script to extract all applications installed on a Mac

  1. MK says:

    It seems that System Information will only save as an spx file now (save/save as yields no options for any type otherwise – just hide/show extension)? Not sure how to get the xsd file you recommend in order to use your script…? People claim it would save a text, RTF or XML, but apparently that was pre-Mavericks or back when it was System Profiler, maybe?

  2. MK says:

    Ah, worked just as well on the only format available from System Information (formerly Profiler) these days…SPX. I just had to remove an errant $nbsp; and change 3@ spaces to actual tabs after copying and pasting and then Data–>From Text worked great in Excel 2016.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s