How to parse/pipe cmd line output using Powershell to an object

Viewed 18019

I have an *.exe that outputs this data when I run this PowerShell command:

& $myExe list

Where $myExe is something like C:\Temp\MyExe.exe and list is an argument.

List of Runbook ID on the system: 



List of services installed on the system: 

ALMService   Version: 7.0.4542.16189
AOSService   Version: 7.0.4542.16189
BIService    Version: 7.0.4542.16189
DevToolsService  Version: 7.0.4542.16189
DIXFService  Version: 7.0.4542.16189
MROneBox     Version: 7.1.1541.3036
PayrollTaxModule     Version: 7.1.1541.3036
PerfSDK  Version: 7.0.4542.16189
ReportingService     Version: 7.0.4542.16189
RetailCloudPos   Version: 7.1.1541.3036
RetailHQConfiguration    Version: 7.1.1541.3036
RetailSDK    Version: 7.1.1541.3036
RetailSelfService    Version: 7.1.1541.3036
RetailServer     Version: 7.1.1541.3036
RetailStorefront     Version: 7.1.1541.3036
SCMSelfService   Version: 7.1.1541.3036

The data I'm looking for is the first column of the table, but it has things like List of Runbook ID... at the top. Is there a good way in PowerShell to parse this data so I can get just the table data?

3 Answers

This will parse it into objects:

$String = @'
List of Runbook ID on the system: 



List of services installed on the system: 

ALMService   Version: 7.0.4542.16189
AOSService   Version: 7.0.4542.16189
BIService    Version: 7.0.4542.16189
DevToolsService  Version: 7.0.4542.16189
DIXFService  Version: 7.0.4542.16189
MROneBox     Version: 7.1.1541.3036
PayrollTaxModule     Version: 7.1.1541.3036
PerfSDK  Version: 7.0.4542.16189
ReportingService     Version: 7.0.4542.16189
RetailCloudPos   Version: 7.1.1541.3036
RetailHQConfiguration    Version: 7.1.1541.3036
RetailSDK    Version: 7.1.1541.3036
RetailSelfService    Version: 7.1.1541.3036
RetailServer     Version: 7.1.1541.3036
RetailStorefront     Version: 7.1.1541.3036
SCMSelfService   Version: 7.1.1541.3036
'@

$String -split '\r?\n' | Select-Object -Skip 6 | ForEach-Object {
    if ($_ -match '^\s*(?<Name>.+?)Version:\s*(?<Version>[\d.]+)\s*$') {
        [PSCustomObject]@{
            Name = $Matches['Name'].TrimEnd()
            Version = $Matches['Version']
        }
    }
    else {
        Write-Verbose -Verbose "Line didn't match. (Line: '$_')"
    }
}

Output from my system:

PS /home/joakim/Documents> ./exe_output.ps1


Name                      Version
----                      -------
ALMService                7.0.4542.16189
AOSService                7.0.4542.16189
BIService                 7.0.4542.16189
DevToolsService           7.0.4542.16189
DIXFService               7.0.4542.16189
MROneBox                  7.1.1541.3036
PayrollTaxModule          7.1.1541.3036
PerfSDK                   7.0.4542.16189
ReportingService          7.0.4542.16189
RetailCloudPos            7.1.1541.3036
RetailHQConfiguration     7.1.1541.3036
RetailSDK                 7.1.1541.3036
RetailSelfService         7.1.1541.3036
RetailServer              7.1.1541.3036
RetailStorefront          7.1.1541.3036
SCMSelfService            7.1.1541.3036

There are some assumptions. The main one is that a "version" can only consist of digits or periods. Include other characters in the character class as needed. The character class here is "[\d.]" - and remember that you need to escape periods to match literal periods rather than "any character" (except newlines (without the (?s) flag)) outside of a character class (stuff between "[" and "]", a meta language within the regex language).

Otherwise, it should be quite robust to minor changes.

For instance, if somehow a space or tab shows up at the beginning of the strings, this is handled with the always-matching "\s*" first ("paranoid parsing", where this kind of "paranoia" is beneficial for robust code).

Related