I am attempting to put the output of compare-object. I am new to Powershell and unfortunately don't know the ins and outs yet.
My command is as follows:
Compare-Object -referenceObject $(Get-Content "c:\temp\mek\123-first.txt") -differenceObject $(Get-Content "c:\temp\mek\123-second.txt") | %{$_.Inputobject} | sort-object | out-file "c:\temp\mek\results.txt"
The contents of my files are as follows (simply comparing Windows services):
systemname name state startmode ---------- ---- ----- --------- D7MCYP AdobeARMservice Stopped Auto D7MCYP AdobeFlashPlayerUpdateSvc Stopped Manual D7MCYP AeLookupSvc Stopped Manual
My results of the compare-object are as follows:
BL3C4V wudfsvc Stopped Auto BL3C4V wudfsvc Stopped Manual D7MCYP AdobeARMservice Running Auto D7MCYP AdobeARMservice Stopped Auto
Now if anyone could help output to keep the first 2 columns per server and the different values of columns 3,4 to new columns (5,6). It would also be nice if I get titles too. For example:
Server Service Before State Before Mode After State After Mode BL3C4V wudfsvc Stopped Auto Stopped Manual D7MCYP AdobeARMservice Running Auto Stopped Auto
Note: The code below is an exercise in parsing plain-text data into objects for more robust, flexible handling.
Ideally, however, processing should start out with objects rather than plain text,
which is why starting with PowerShell cmdlets such as Get-Service
rather than the text output from external utilities is preferable.
Assuming that all entries in each input file have a matching server + service-name entry in the respective other file:
$f1, $f2 = "c:\temp\mek\123-first.txt", "c:\temp\mek\123-second.txt"
Compare-Object (Get-Content $f1) (Get-Content $f2) | ForEach-Object {
$i = 0; $ht = @{}; $vals = -split $_.InputObject
foreach($col in 'Server', 'Service', 'State', 'Mode') {
$ht.$col = $vals[$i++]
}
$ht.Before = $_.SideIndicator -eq '<='
[pscustomobject] $ht
} | Group-Object Server, Service | ForEach-Object {
$ndxBefore, $ndxAfter = if ($_.Before) { 0, 1 } else { 1, 0 }
[pscustomobject] @{
Server = $_.Group[0].Server
Service = $_.Group[0].Service
'State Before' = $_.Group[$ndxBefore].State
'Mode Before' = $_.Group[$ndxBefore].Mode
'State After' = $_.Group[$ndxAfter].State
'Mode After' = $_.Group[$ndxAfter].Mode
}
} | Sort-Object Server, Service |
Format-Table
Note:
The above formats the output for display (using Format-Table
), without sending it to a file.
You can append | Out-File "c:\temp\mek\results.txt"
to save the same representation to a file.
However, note that the command - before Format-Table
is applied - returns objects with individual properties, so you can output to a file in a variety of formats, such as by using Export-Csv
, for instance.
Example output:
Server Service State Before Mode Before State After Mode After
------ ------- ------------ ----------- ----------- ----------
D7MCYP AdobeFlashPlayerUpdateSvc Stopped Manual Stopped Auto
D7MCYP AeLookupSvc Stopped Manual Started Manual
Explanation:
A single, long pipeline is used, which makes the code concise and memory-efficient.
The pipeline breaks down as follows:
Comparison:
Compare-Object
compares the array of lines from the two input files returned by the Get-Content
calls, and outputs [pscustomobject]
instances representing the differences found, with string property .SideIndicator
indicating whether the line at hand (accessible via .InputObject
) is unique to the LHS (the 1st input file) - <=
- or the RHS (2nd input file) - >=
Transformation to custom objects:
The script block ({ ... }
) passed to ForEach-Object
is executed for each input object (represented as $_
).
-split $_.InputObject
splits the "difference line" at hand into fields by runs of whitespace and stores the resulting fields as an array in $vals
.
$ht
is an auxiliary hashtable that is used to map the field values to field names.
$ht.Before
adds a Boolean entry to indicate whether the difference line at hand is from the "before file" (the 1st input file) or not.
[pscustomobject] $ht
converts the aux. hashtable into a custom object and outputs it (sends it through the pipeline).
Grouping:
Group-Object
is used to group the resulting objects can by shared Server
and Service
property values, resulting in a [Microsoft.PowerShell.Commands.GroupInfo]
instance representing each grouping.Transformation to combined custom objects:
Again, ForEach-Object
is used to perform per-input object processing.
[pscustomobject] @{ ... }
is used to construct each combined output object, again using an auxiliary hashtable.
$_.Group
contains the input objects that form each group - in our case, $_.Group[0]
and $_.Group[1]
are the converted-to-objects input lines representing a given server-service combination.
By definition, both input objects have the same .Server
and .Service
values, so blindly using $_.Group[0]
's values for the combined output object will do.
By contrast, the * Before
and * After
properties the appropriate input object (whether from the 1st or 2nd file), which is why the array indices $ndxBefore
and $ndxAfter
are chosen accordingly, via the previously added .Before
property
Sorting:
Sort-Object
sorts the resulting objects by the specified properties.Output formatting:
Format-Table
ensures that the sorted objects are presented as a table.