2

我有一个 CSV 文件,其中在某些单元格中包含多行。我将使用这些数据来比较从 powershell 获得的值。

这将返回对象之间的差异,但值是相同的。

预期结果不应返回任何内容,因为两个值相同。

CSV 内容:

价值
System\CurrentControlSet\Control\ProductOptions
System\CurrentControlSet\Control\Server 应用程序
软件\Microsoft\Windows NT\CurrentVersion

代码:

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
4

2 回答 2

3

这篇文章不再直接回答 OP 的问题,而是提供有助于类似情况的背景信息。通过在比较数据之前处理 CR 和 LF 字符来解决这个特定问题。有关详细信息,请参阅标记答案

由于$data在这种情况下是一个具有名为的属性的对象value来保存您的数据,因此您需要将该value属性中存储的内容与您的$regval:

Compare-Object $data.value $regval

$regval是一个字符串数组,然后将其通过管道传输到Out-String. 在管道之后,它就变成了一个字符串对象。在管道连接到 之前,请参见下面的类型信息Out-String

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

$data是一个对象数组 (PSCustomObjects),每个对象都有一个名为的属性Value,如果您想要其数据,则需要直接引用该属性:

$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;}

为了使用 比较两个对象的数据,Compare-Object当对象是相同类型的集合时,似乎会出现最好的结果。在某些情况下,PowerShell 会自动在后台进行转换,例如Compare-Object "1" 1. 也许这与值类型有关,因为我不完全确定。在将您的任何数据转换为不同类型之前, 我会进行比较。然后,如果您引用 的属性,则此条件变为真:Value$data

$data.value | Get-member -type Property

   TypeName: System.String

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

有关 PowerShell 如何处理数组类型的更多信息,您可以参考MKlement0 的说明。

于 2019-03-28T10:43:12.990 回答
1

最可能的解释是:

  • CSV中的多行值(从单个字段获得)包含仅 LF(Unix 样式)换行符,
  • 而从注册表值派生的字符串由于应用于字符串数组而具有CRLF (Windows 样式) 换行符Out-String

最直接的解决方法是删除 CR 字符。来自$regval(您可以"`r"在 PowerShell 中使用来生成 CR 字符):

# ...

# 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

那说:

  • 由于您只比较两个字符串对象,因此您可以避免开销Compare-Object并简单地使用-eq

    $data.Value -eq $regVal
    
  • 或者,您可以将多行值拆分为行数组并单独比较它们;请注意,如果您使用正则表达式"`r?`n"或 ( '\r?\n') 来匹配换行符以进行拆分 - 它同时匹配 LF-only 和 CRLF 换行符 - 您不需要删除 CR 字符。事先甚至应用于Out-StringGet-ItemProperty HKLM:\...调用开始的数组输出;但是,使用您问题中的变量值,您可以使用:

    # 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
    

至于你尝试了什么:

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

If$data.Value是一个没有尾随换行符的单个字符串- 无论它是否嵌入了换行符 - 以上是一个有效的 no-op

  • 已经是字符串的输入对象按原样通过Out-String
  • 虽然Out-String确实附加了一个尾随 CRLF 换行符(在 Windows 上),但随后的.Trim()调用再次将其删除。
于 2019-03-28T13:18:27.613 回答