比较 2 个 Excel 文件并仅输出差异(Powershell)

2023-12-29

我正在尝试比较 2 个 XLXS,并且只想输出差异。这可以用 Compare-Object 来完成吗?

我目前正在使用这个有效的脚本,但它没有捕获我真正需要的内容:

#IMPORT PS EXCEL MODULE 
import-module psexcel
#FILE TO BE COMPARED VARIABLES
$file1 = Import-XLSX C:\CurrentFile.xlsx
$file2 = Import-XLSX C:\PreviousFile.xlsx

#FILTER TO GET DATA ONLY FROM THE REFERENCE FILE ONLY
filter leftside{
param(
        [Parameter(Position=0, Mandatory=$true,ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [PSCustomObject]
        $obj
    )

    $obj|?{$_.sideindicator -eq '<='}
}

#PROPERTY VARIABLE
$property = "Name" , "UserPrincipalName" , "NickName" , "Age" , "Sex"

#COMPARISON PROPER
$Comparison = Compare-Object -referenceobject $file1 -differenceobject $file2 -SyncWindow ($file1.Length / 2) -property $property | leftside

示例文件如下所示:

PreviousFile:

Name        UserPrincipalName   NickName    Age Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20  M
UserUser 2  [email protected] /cdn-cgi/l/email-protection       User2       20  M
UserUser 3  [email protected] /cdn-cgi/l/email-protection       User3       20  M
UserUser 4  [email protected] /cdn-cgi/l/email-protection       User90      20  M
UserUser 5  [email protected] /cdn-cgi/l/email-protection       User91      20  M

CurrentFile:

Name        UserPrincipalName   NickName    Age Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20  M
UserUser 2  [email protected] /cdn-cgi/l/email-protection      User2       20  M
UserUser 3  [email protected] /cdn-cgi/l/email-protection      User3       20  M
UserUser 4  [email protected] /cdn-cgi/l/email-protection      User4       20  M
UserUser 5  [email protected] /cdn-cgi/l/email-protection      User5       20  M

差异:UserPrincipalName 下的所有项目,除了[电子邮件受保护] /cdn-cgi/l/email-protection

昵称 User90-User4 、 User91-User5

使用当前脚本输出:

输出显示存在差异的行的完整信息。

Name        UserPrincipalName   NickName    Age Sex
UserUser 2  [email protected] /cdn-cgi/l/email-protection      User2       20  M
UserUser 3  [email protected] /cdn-cgi/l/email-protection      User3       20  M
UserUser 4  [email protected] /cdn-cgi/l/email-protection      User4       20  M
UserUser 5  [email protected] /cdn-cgi/l/email-protection      User5       20  M
UserUser 6  [email protected] /cdn-cgi/l/email-protection      User6       20  M

预期输出:

Name        UserPrincipalName   NickName    Age Sex
UserUser 1              
UserUser 2  [email protected] /cdn-cgi/l/email-protection                  
UserUser 3  [email protected] /cdn-cgi/l/email-protection                  
UserUser 4  [email protected] /cdn-cgi/l/email-protection                  
UserUser 5  [email protected] /cdn-cgi/l/email-protection      User5           
UserUser 6  [email protected] /cdn-cgi/l/email-protection      User6       

我希望脚本始终输出我的标识符,在本例中为“名称”,我也希望它仅输出差异。如果在旧文件和当前文件之间发现任何更改,则仅显示发现差异的当前文件。

有没有办法可以这样输出?

任何帮助,任何协助,将不胜感激。谢谢你!


经过进一步分析。我的例子可能搞砸了。 确实,我每次都需要输出我的标识符。但我需要它在有差异的条目上输出only即使我的标识符没有改变。

这是一个新的例子:

PreviousFile:

Name        UserPrincipalName   NickName    Age Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20  M
UserUser 2  [email protected] /cdn-cgi/l/email-protection      User2       20  F
UserUser 3  [email protected] /cdn-cgi/l/email-protection      UserC       20  M
UserUser 4  [email protected] /cdn-cgi/l/email-protection       User4       20  M
UserUser 5  [email protected] /cdn-cgi/l/email-protection       User5       20  M

CurrentFile:

Name        UserPrincipalName   NickName    Age Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20  M
UserUser 2  [email protected] /cdn-cgi/l/email-protection      User2       20  M
UserUser 3  [email protected] /cdn-cgi/l/email-protection      User3       20  M
UserUser 4  [email protected] /cdn-cgi/l/email-protection      User4       20  M
UserUser 5  [email protected] /cdn-cgi/l/email-protection      User5       20  M
UserUser 6  [email protected] /cdn-cgi/l/email-protection      User6       20  M




Expected output:

Name        UserPrincipalName   NickName    Age Sex             
UserUser 2                                      F
UserUser 3                      UserC  
UserUser 4  [email protected] /cdn-cgi/l/email-protection                  
UserUser 5  [email protected] /cdn-cgi/l/email-protection      
UserUser 6  [email protected] /cdn-cgi/l/email-protection      User6       20  M

UserUser1 根本没有条目,因为一切都匹配

UserUser2已出现在列表中,因为性别更改为F

UserUser3,昵称为 UserC

UserUser4 和 5 的用户主体名称已更新为 @user.com

Useruser6 是一个新对象

所有有任何差异的条目将仅显示标识符“名称”和差异。

我尝试了一下你的代码,但我无法得到这个输出。老实说,我还不明白你剧本中发生的一切。

我希望你也能帮我解决这个问题。

谢谢你,祝你有美好的一天!


对于这些更定制的比较输出,我建议放弃Compare-Object自由式地控制你想要的东西。

一旦我们确定了如何匹配两个数据集之间的记录,我们就可以循环当前对象的每个属性,并将它们与前一个文件中匹配对象的相同属性进行比较。如果值匹配,那么我们应该输出一个空字符串。如果它们不匹配,我们应该输出当前对象的值。我们将始终输出我们选择匹配的属性的值,在本例中是“Name”属性。

# TEST DATA
$previousFile = @'
Name        UserPrincipalName   NickName    Age  Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20   M
UserUser 2  [email protected] /cdn-cgi/l/email-protection       User2       20   M
UserUser 3  [email protected] /cdn-cgi/l/email-protection       User3       20   M
UserUser 4  [email protected] /cdn-cgi/l/email-protection       User90      20   M
UserUser 5  [email protected] /cdn-cgi/l/email-protection       User91      20   M
'@

$currentFile = @'
Name        UserPrincipalName   NickName    Age  Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20   M
UserUser 2  [email protected] /cdn-cgi/l/email-protection      User2       20   M
UserUser 3  [email protected] /cdn-cgi/l/email-protection      User3       20   M
UserUser 4  [email protected] /cdn-cgi/l/email-protection      User4       20   M
UserUser 5  [email protected] /cdn-cgi/l/email-protection      User5       20   M
UserUser 6  [email protected] /cdn-cgi/l/email-protection      User6       20   M
'@

$previous = $previousFile -replace ' {2,}', ',' | ConvertFrom-Csv
$current = $currentFile -replace ' {2,}', ',' | ConvertFrom-Csv
# END TEST DATA

# Set which property should be used to match records from current to previous
$matchPropertyName = 'Name'

$current | ForEach-Object {
    $obj = $_
    $match = $previous.where({ $_.$matchPropertyName -eq $obj.$matchPropertyName })
    if ($match) {
        $out = [ordered]@{}

        # loop through all the properties in current objects to determine whether to show them or not
        $obj.psobject.Properties | ForEach-Object {
            # if property = 'Name' then include the value as record Identifier
            if ($_.Name -eq $matchPropertyName) { $out[$_.Name] = $_.Value }

            # else if value of property in current matches previous set value to empty string
            elseif ($_.Value -eq $match[0].($_.Name)) {
                $out[$_.Name] = ''
            }

            # else current value does not match previous value so output current value
            else {
                $out[$_.Name] = $_.Value
            }
        }
        [pscustomobject]$out
    }

    # if not matched by 'Name' property then consider as new object and output all values
    else {
        $obj
    }
}

Output

Name       UserPrincipalName NickName Age Sex
----       ----------------- -------- --- ---
UserUser 1
UserUser 2 [email protected] /cdn-cgi/l/email-protection
UserUser 3 [email protected] /cdn-cgi/l/email-protection
UserUser 4 [email protected] /cdn-cgi/l/email-protection    User4        
UserUser 5 User[email protected] /cdn-cgi/l/email-protection    User5
UserUser 6 [email protected] /cdn-cgi/l/email-protection    User6    20  M

关于此解决方案需要注意的一件事是,它没有考虑已删除的行(即,先前存在但已在当前中删除的行),尽管这似乎是您在自己的示例中通过过滤所要实现的仅适用于左侧。


UPDATE

为了响应您的附加信息,这里提供了一个更新的解决方案,其中不包括未更改的记录,并包含更多详细信息,希望可以帮助您更好地了解正在发生的情况。为了不输出未更改的对象,在循环和比较对象时,我们可以跟踪变量中的对象是否已更改,然后仅在知道发生更改时才输出该对象。

# TEST DATA
$previousFile = @'
Name        UserPrincipalName   NickName    Age  Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20   M
UserUser 2  [email protected] /cdn-cgi/l/email-protection      User2       20   M
UserUser 3  [email protected] /cdn-cgi/l/email-protection      User3       20   M
UserUser 4  [email protected] /cdn-cgi/l/email-protection      User4       20   M
UserUser 5  [email protected] /cdn-cgi/l/email-protection      User5       20   M
UserUser 6  [email protected] /cdn-cgi/l/email-protection      User6       20   M
'@

$currentFile = @'
Name        UserPrincipalName   NickName    Age  Sex
UserUser 1  [email protected] /cdn-cgi/l/email-protection      User1       20   M
UserUser 2  [email protected] /cdn-cgi/l/email-protection      User2       20   M
UserUser 3  [email protected] /cdn-cgi/l/email-protection      User3       20   F
UserUser 4  [email protected] /cdn-cgi/l/email-protection      User_4       20   M
UserUser 5  [email protected] /cdn-cgi/l/email-protection      User_5       20   M
UserUser 6  [email protected] /cdn-cgi/l/email-protection      User_6       30   M
'@

$previous = $previousFile -replace ' {2,}', ',' | ConvertFrom-Csv
$current = $currentFile -replace ' {2,}', ',' | ConvertFrom-Csv
# END TEST DATA

# Set which property should be used to match records from current to previous
$matchPropertyName = 'Name'

$current | ForEach-Object {
    # in ForEach-Object scriptblock $_ is the current object being processed.
    # Below we assign $_ to $obj in order to reference it in the
    # $previous.where({}) scriptblock where $_ will reference it's own object
    # from the $previous array
    $obj = $_

    # Here we are looking for an object in $previous array where property $matchPropertyName, which we have
    # defined earlier as being equal to 'Name', matches our current object's 'Name' property.  We stick this match
    # in a variable called $match
    $match = $previous.where({ $_.$matchPropertyName -eq $obj.$matchPropertyName })

    # if we find a matching record in $previous
    if ($match) {
        # We will use $isModified to keep track of whether a changes was found in any of the properties
        $isModified = $false

        # create an ordereddictionary in which to build our own object with
        # we use this to capture the property names and values using key-value pairs
        # $out[<keyname>] = <value>
        # later we can easily convert this to an object using the [pscustomobject] type accelerator
        $out = [ordered]@{}

        # Loop through all the properties in current object to determine whether to show them or not.
        # Here we use the an intrinsic member called .psobject to access a collection containing all
        # the object's properties and values and loop through each of them.
        # i.e., we loop through objects that look like this
        #
        # Name              Value
        # ----              -----
        # Name              UserUser 1
        # UserPrincipalName [email protected] /cdn-cgi/l/email-protection
        # NickName          User1
        # Age               20
        # Sex               M   
        
        # there are 5 properties so we will be looping 5 times in the following ForEach-Object call, 
        # once for each property of the current object
        $obj.psobject.Properties | ForEach-Object {
            # if property = 'Name' then include the value as record Identifier
            if ($_.Name -eq $matchPropertyName) { $out[$_.Name] = $_.Value }

            # else if value of property in current matches previous set value to empty string
            elseif ($_.Value -eq $match[0].($_.Name)) {
                $out[$_.Name] = ''
            }

            # else current value does not match previous value so output current value
            else {
                # create a key-value pair in our dictionary containing the property name and value
                # e.g., $out['NickName'] = 'User1'
                $out[$_.Name] = $_.Value

                # update $isModified to true so we know to include in the output
                $isModified = $true
            }
        }

        # Check if the object has been modified and if so, output it.
        # We use the [pscustomobject] type accelerator here to convert our ordereddictionary to an object
        # containing our key-value pairs as properties.
        if ($isModified) { [pscustomobject]$out }
    }

    # if we don't find a matching object in $previous then consider as new object and output all values
    else {
        $obj
    }
}

参考:

  • .psobject 内部成员 https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.2#remove-properties
  • [pscustomobject] 类型加速器 https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.2#converting-a-hashtable
  • [订购]型加速器 https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.2#ordered-hashtables
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

比较 2 个 Excel 文件并仅输出差异(Powershell) 的相关文章

随机推荐

  • Java applet 清单 - 允许所有 Caller-Allowable-Codebase

    从 Java 7u45 开始 如果网页尝试通过 javascript 与小程序交互 并且该页面未在清单的 Caller Allowable Codebase 属性中列出 则小程序将显示警告消息 即使使用受信任的证书进行签名 有关此更改的发行
  • 是否抛出 ConcurrentModificationException 取决于系统

    我正在使用 Iterator 编写一段代码 当我在 Windows 上从 IDE 运行该程序时 在 a 行收到 ConcurrentModificationException LinkedList ll new LinkedList Ite
  • 是否应该在编写代码之前先编写单元测试?

    我知道测试驱动开发的定义原则之一是首先编写单元测试 然后编写代码来通过这些单元测试 但是有必要这样做吗 我发现 在编写之前 我常常不知道自己在测试什么 主要是因为我过去从事的几个项目更多地是从概念验证演变而来 而不是设计出来的 我之前曾尝试
  • UNIQUE 约束失败

    我正在使用 Django 进行 Tango 但无法解决这个练习 我明白了django db utils IntegrityError UNIQUE constraint failed rango category name错误 这是我尝试实
  • IE 浏览器控件 res:// 用法

    我在我的应用程序中使用 IWebBrowser2 控件 并且有各种 html 文件作为资源存储在 exe 中 为了加载这些 我使用 res 协议 问题是 对于某些版本的 IE 页面不再加载 而只是显示 操作已取消 Internet Expl
  • 如何根据另一个矩阵、data.frame 或向量的行重新排序

    test1 lt as matrix c 1 2 3 4 5 row names test1 lt c a d c b e test2 lt as matrix c 6 7 8 9 10 row names test2 lt c e d c
  • 为我的 numpy 数组提供更紧凑的 __repr__ 吗?

    当我显示数组时 默认 repr 方法用于ndarray对象对于我想做的事情来说太大了 a np eye 32 b hello 42 array a b 产生 array array 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1
  • MongoDB 选择 _id 数组中的哪个位置?

    在 mongo db 中可以像 SQL 一样选择集合的文档 SELECT FROM collection WHERE id IN 1 2 3 4 或者如果我有一个 id array我必须一一选择 然后重新组合array object结果 E
  • 从命令行启动进程时如何捕获进程的 PID?

    有没有办法纯粹在 bat 文件中执行此操作 目的是推出iexplore exe 然后在完成时杀死该实例 这是我使用的 echo off rem there is a tab in the file at the end of the lin
  • 注释声明中 String[] 的默认“”是什么?

    什么是default 下面代码中的部分是什么意思 public interface MyAnnotation String names default 是否等于 String names default new String 0 publi
  • 将响应发送给除发件人之外的所有客户端

    要将某些内容发送给所有客户 您可以使用 io sockets emit response data 要接收来自客户的信息 您可以使用 socket on cursor function data 如何将两者结合起来 以便在服务器上从客户端接
  • enctype“application/json”形式可用吗?

    我正在读这个w3c 文档 https www w3 org TR html json forms 关于用 html 表单发布 JSON 数据 并尝试测试它 我的测试表格如下
  • gcc -O0 在 2 的幂矩阵大小(矩阵转置)上优于 -O3

    出于测试目的 我编写了一个简单的方法来计算 nxn 矩阵的转置 void transpose const size t n double A for uint i 0 i lt n i for uint j i 1 j lt n j dou
  • JPEG 伪影检测

    是否有已知的算法可以在不查看图像的情况下以编程方式检测图像退化 我考虑有损重新编码的明显 可见 图像伪影 例如颜色 失真 边缘噪声 块效应等 例如 从原始源编码且 JPEG 质量为 80 的图像就可以 我希望这是正确的提问地点 但如果版主认
  • Lua - 删除非空目录

    我正在尝试删除中的非空目录Lua但没有成功 我尝试了以下方法 os remove path to dir 并得到错误 Directory not empty 39当文件数为39时path to dir 还尝试过 require lfs lf
  • 如何为时间序列中缺失的数据创建“NA”

    我有几个数据文件 如下所示 X code year month day pp 1 4515 1953 6 1 0 2 4515 1953 6 2 0 3 4515 1953 6 3 0 4 4515 1953 6 4 0 5 4515 19
  • 行内元素内的绝对定位。这种行为正确吗?

    考虑以下简单的 HTML 和 CSS a rel position relative button position absolute top 0 left 0 Lorem ipsum dolor sit amet a class rel
  • 如何忽略 Git 上的 IDE 设置?

    我有以下 Git 信息 我想忽略 IDE Eclipse 的设置 modified myproject classpath modified myproject project modified myproject settings com
  • python中是否存在空类?

    python中是否存在特殊的类来创建空对象 我尝试了 object 但它不允许我添加字段 我想这样使用它 obj EmptyObject obj foo far obj bar boo 我应该每次 在几个独立的脚本中 定义这样的新类吗 cl
  • 比较 2 个 Excel 文件并仅输出差异(Powershell)

    我正在尝试比较 2 个 XLXS 并且只想输出差异 这可以用 Compare Object 来完成吗 我目前正在使用这个有效的脚本 但它没有捕获我真正需要的内容 IMPORT PS EXCEL MODULE import module ps