如果我没有列出属性,则Compare-Object无法正常工作

问题描述 投票:7回答:2

我有两个Excel电子表格我想比较:

$OleDbAdapter = New-Object System.Data.OleDb.OleDbDataAdapter “Select * from [Report$]“,”Provider=Microsoft.ACE.OLEDB.12.0;Data Source=S:\FIS-BIC Reporting\Report Output Files\Product-Marketing\TEST_XI\ECM - Pipeline by LOB_04182013_040544.xls;Extended Properties=”"Excel 12.0 Xml;HDR=YES”";”
$RowsReturned = $OleDbAdapter.Fill($DataTable)

$OleDbAdapter2 = New-Object System.Data.OleDb.OleDbDataAdapter “Select * from [Report$]“,”Provider=Microsoft.ACE.OLEDB.12.0;Data Source=S:\FIS-BIC Reporting\Report Output Files\Product-Marketing\ECM - Pipeline by LOB_04182013_074004.xls;Extended Properties=”"Excel 12.0 Xml;HDR=YES”";”
$RowsReturned2 = $OleDbAdapter2.Fill($DataTable2)

Compare-Object $DataTable $DataTable2 

它什么都不返回。我知道在第6栏中,它们是不同的。如果我指定“-property F6”,它确实会返回差异。除非我指定属性,否则任何想法都没有?列数可以变化(虽然比较中的每个文件都相同),因此具体指定属性将不起作用。

powershell powershell-v2.0
2个回答
23
投票

如果未指定-Property参数,则Compare-Object不会比较所有属性,而是比较在两个对象上调用.ToString()方法的结果。因此,Compare-Object $DataTable $DataTable2将$ DataTable1.ToString()与$ DataTable1.ToString()进行比较。在DataTable对象上调用时,.ToString()方法返回一个空字符串,因此报告没有区别。

例如:

$file1 = Get-Item somefilename
$file1 = Get-Item anotherfilename
Compare-Object $file1 $file2

这将返回两个文件的完整路径之间的差异,如下所示:

InputObject              SideIndicator
-----------              -------------
<path>\anotherfilename   =>
<path>\somefilename      <=

这是因为在FileInfo对象上调用.ToString()会返回其FullName属性,因此您要比较文件的完整路径名。

虽然-Property参数接受多个属性,但列出所有属性不是解决方案。除了非常乏味,它不会给你你想要的结果。如果列出多个属性,则Compare-Object会比较所有属性的组合,如果列出的属性中的任何一个不同,则返回显示所有列出属性的结果(两个属性相同且不同的属性)作为单一的差异。

您需要做的是迭代属性列表,并为每个属性调用一次Compare-Object:

$properties = ($DataTable | Get-Member -MemberType Property | Select-Object -ExpandProperty Name)
foreach ($property in $properties) {
  Compare-Object $DataTable $DataTable2 -Property "$property" | Format-Table -AutoSize
}
  • 在大多数情况下,在比较两个对象的所有属性时,您需要使用Get-Member -MemberType Properties,以覆盖所有属性类型。但是,如果您要比较DataTable对象,最好使用Get-Member -MemberType Property,这样您只能比较与数据字段对应的属性,而不是与数据无关的DataTable对象的其他属性。
  • 这是假设列数与您所说的相同,或者至少表示$ DataTable2中的列数不超过$ DataTable中的列数。 如果您无法可靠地假设,通过将($DataTable | Get-Member -MemberType Property).Count($DataTable2 | Get-Member -MemberType Property).Count进行比较并使用更大的属性来从任何具有更多列的$ properties数组派生。
  • 使用Format-Table非常重要,它不仅仅是让事物变得漂亮。如果列出多个相同类型的对象(在本例中为数组),PowerShell会记住第一个对象的格式,并将其用于所有后续对象,除非您明确指定格式。由于第一列的名称对于每个属性(即电子表格中的每一列)将是不同的,因此除了遇到的第一个差异之外,第一列将为空。 -AutoSize开关是可选的。那就是让事情看起来很漂亮。但是您必须将结果传递给格式化过滤器。如果您愿意,也可以使用Format-List。

2
投票

Adi Inbar's helpful answer包含有关Compare-Object如何工作的良好背景信息。

但是,有一种方法可以通常使用-Property来比较所有列值 - 假设两个输入表具有相同的列结构,或者表只应该通过第一个表的列进行比较:

# The original collection.
$coll1 = [pscustomobject] @{ one = '1a'; two = '2a'; three = '3a' },
         [pscustomobject] @{ one = "1b"; two = "2b"; three = '3b' }

# The other collection to compare the original to.
# Note the difference in the 2nd object in column 'two'
$coll2 = [pscustomobject] @{ one = '1a'; two = '2a'; three = '3a' },
         [pscustomobject] @{ one = "1b"; two = "2b!"; three = '3b' }

# PSv3+: Get the array of all property names to compare 
#        from the original collection.
# Note: 
#      * The assumption is that both collections have the same set of 
#        properties (or that the collections should only be compared by
#        the *first* collection's properties).
#      * In PSv2-, use the following instead:
#         $propsToCompare = $coll1[0].psobject.properties | % { $_.name }
$propsToCompare = $coll1[0].psobject.properties.name

# Compare the 2 collections by all property values.
# -PassThru means that any input object in which a difference is found
# is passed through as-is.
Compare-Object $coll1 $coll2 -Property $propsToCompare -PassThru

以上产量:

one two three SideIndicator
--- --- ----- -------------
1b  2b! 3b    =>           
1b  2b  3b    <=           

请注意=>如何告诉您所选对象对于<=而言是右侧专有的,反之亦然。

需要注意的是Compare-Object很慢,因为它无法对输入数据进行排序,因此必须完整地读取和比较两个输入集合。

使用排序输入,您可以使用-SyncWindow <Int32>来加快速度,但这需要提前了解两个输入集合之间最多可以有多少项目,发现每个差异,如果-SyncWindow值太小,则会出现虚假差异报道。

© www.soinside.com 2019 - 2024. All rights reserved.