2

I have a CSV file which contains multiline in some cells. I will use this data to compare the value got it from powershell.

This returns differences between the object, however the value is the same.

Expected Results should return nothing because both values are the same.

CSV content:

Value
System\CurrentControlSet\Control\ProductOptions
System\CurrentControlSet\Control\Server Applications
Software\Microsoft\Windows NT\CurrentVersion

Code:

PS> $data = Import-Csv .\tr.csv

PS> $data.Value
System\CurrentControlSet\Control\ProductOptions
System\CurrentControlSet\Control\Server Applications
Software\Microsoft\Windows NT\CurrentVersion

PS> $regval = ((Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg\AllowedExactPaths).machine | Out-String).Trim()

PS> $regval
System\CurrentControlSet\Control\ProductOptions
System\CurrentControlSet\Control\Server Applications
Software\Microsoft\Windows NT\CurrentVersion

PS> Compare-Object $data.Value $regval
System\CurrentControlSet\Control\ProductOptions...                                                                                                  =>
System\CurrentControlSet\Control\ProductOptions...                                                                                                  <=

PS> $Tostring = ($data.Value | out-string).Trim()

PS> Compare-Object $Tostring $regval

InputObject                                                                                                                                         SideIndicator
-----------                                                                                                                                         -------------
System\CurrentControlSet\Control\ProductOptions...                                                                                                  =>
System\CurrentControlSet\Control\ProductOptions...                                                                                                  <=


PS> $Tostring.Length
145

PS> $regval.Length
147
mklement0
  • 382,024
  • 64
  • 607
  • 775

2 Answers2

3

This post no longer answers the OP's question directly but provides background information that is helpful for similar situations. This particular issue is solved by handling CR and LF characters before comparing the data. See Marked Answer for details.

Since $data in this case is an object with a property called value that holds your data, you need to compare what is stored in the value property to your $regval:

Compare-Object $data.value $regval

$regval is an array of strings before you pipe it to Out-String. After the pipe, it then becomes a string object. See below for type information before piping to Out-String.

$regval.gettype().fullname
System.String[]

$data is an array of objects (PSCustomObjects), which each have a property called Value that needs to be referenced directly if you want its data:

$data.gettype().fullname
System.Object[]

$data | Get-Member

   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Value       NoteProperty string Value=System\CurrentControlSet\Control\ProductOptions

($regval | Get-member).where({$_.MemberType -eq "Property"})

   TypeName: System.String

Name   MemberType Definition
----   ---------- ----------
Length Property   int Length {get;}

In order to compare the data of two objects using Compare-Object, best results seem to come when the objects are collections of the same type. PowerShell will automatically do conversions in the background in some cases like Compare-Object "1" 1. Maybe that has something to do with value types as I am not entirely sure. I would do the comparison before converting any of your data to different types. Then if you reference the Value property of $data, this condition becomes true:

$data.value | Get-member -type Property

   TypeName: System.String

Name   MemberType Definition
----   ---------- ----------
Length Property   int Length {get;}

You can reference MKlement0's Explanation for more information about how PowerShell handles the array type.

AdminOfThings
  • 23,946
  • 4
  • 17
  • 27
  • Thanks for updating. Re `Compare-Object "1" 1`: From what I can tell, value types and strings are coerced to the type of the LHS (`-ReferenceObject`). Compare `Compare-Object 10 '0xa'` to `Compare-Object '0xa' 10`. You can simplify your `Get-Member` command to `$regval | Get-Member -Type Properties` – mklement0 Mar 28 '19 at 14:43
1

The likeliest explanation is that:

  • the multi-line value from your CSV (obtained from a single field) contains LF-only (Unix-style) newlines,
  • whereas the string derived form the registry values has CRLF (Windows-style) newlines, due to applying Out-String to an array of strings.

The most direct fix is to remove the CR chars. from $regval (you can use "`r" in PowerShell to generate a CR char):

# ...

# Remove all CRs from $regval.
# Note that not providing a replacement string (missing 2nd RHS operand)
# default to the empty string, which effectively *removes* what was matched.
$regval = $regval -replace "`r"

# Should now work as expected.
Compare-Object $data.Value $regval

That said:

  • Since you're comparing just two objects that are strings, you can avoid the overhead of Compare-Object and simply use -eq:

    $data.Value -eq $regVal
    
  • Alternatively, you can split the multi-line values into arrays of lines and compare them individually; note that if you use regex "`r?`n" or ('\r?\n') to match newlines to split by - which matches both LF-only and CRLF newlines - you needn't remove CR chars. beforehand or even apply Out-String to the array output from the Get-ItemProperty HKLM:\... call to begin with; however, with the variable values from your question, you'd use:

    # Newline-style-agnostic
    Compare-Object ($data.Value -split "`r?`n") ($regval -split "`r?`n")
    
    # Or, knowing that $data.Value has LF, and $regval CRLF
    Compare-Object ($data.Value -split "`n") ($regval -split "`r`n")
    
    # Or, by using the [string[]] array of registry values directly:
    $regvals = (Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg\AllowedExactPaths).machine
    Compare-Object ($data.Value -split "`n") $regvals
    

As for what you tried:

$Tostring = ($data.Value | out-string).Trim()

If $data.Value is a single string that doesn't have a trailing newline - whether or not it has embedded newlines - the above is an effective no-op:

  • An input object that is already a string is passed through as-is by Out-String.
  • While Out-String does append a trailing CRLF newline (on Windows), the subsequent .Trim() call removes it again.
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Awesome man!! i have been trying all the possibilities and im close your explanation. Can you please guide me where to check more details about LF and CRF things. Thanks a ton. – shaik abdul Mar 28 '19 at 13:31
  • My pleasure, @shaikabdul. I'd start here with general information: https://en.wikipedia.org/wiki/Newline. Here are some PowerShell-specific answers related to newlines: https://stackoverflow.com/a/46270751/45375 https://stackoverflow.com/a/45303612/45375 https://stackoverflow.com/a/48919146/45375 https://stackoverflow.com/a/48388980/45375 – mklement0 Mar 28 '19 at 13:50