在 Powershell 中,将两个表合并为一个的最佳方法是啥?

Posted

技术标签:

【中文标题】在 Powershell 中,将两个表合并为一个的最佳方法是啥?【英文标题】:In Powershell, what's the best way to join two tables into one?在 Powershell 中,将两个表合并为一个的最佳方法是什么? 【发布时间】:2010-12-23 08:36:35 【问题描述】:

我是 Powershell 的新手,想知道是否有人知道完成以下示例问题的更好方法。

我有一组从 IP 地址到主机名的映射。这表示活动的 DHCP 租约列表:

PS H:\> $leases

IP                    Name
--                    ----
192.168.1.1           Apple
192.168.1.2           Pear
192.168.1.3           Banana
192.168.1.99          FishyPC

我还有另一组从 MAC 地址到 IP 地址的映射。这表示 IP 保留列表:

PS H:\> $reservations

IP                    MAC
--                    ---
192.168.1.1           001D606839C2
192.168.1.2           00E018782BE1
192.168.1.3           0022192AF09C
192.168.1.4           0013D4352A0D

为方便起见,我能够使用以下代码生成第三组从 MAC 地址到 IP 地址和主机名的映射。这个想法是$reservations 应该获得第三个字段“名称”,只要有匹配的“IP”字段,就会填充该字段:

$reservations = $reservations | foreach 
    $res = $_
    $match = $leases | where $_.IP -eq $res.IP | select -unique
    if ($match -ne $NULL) 
        "" | select @n="IP";e=$res.IP, @n="MAC";e=$res.MAC, @n="Name";e=$match.Name
    

想要的输出是这样的:

PS H:\> $ideal

IP                    MAC                 Name
--                    ---                 ----
192.168.1.1           001D606839C2        Apple
192.168.1.2           00E018782BE1        Pear
192.168.1.3           0022192AF09C        Banana
192.168.1.4           0013D4352A0D

有没有更好的方法?

【问题讨论】:

令人难以置信的是,PowerShell 中还没有包含这个功能 @Michael 您如何获得所有活动租约的 IP 地址到主机名的表?我一直在使用 DHCP 模块,但无法弄清楚。我也在尝试合并一些表格。 @Ruisu 我使用netsh 的 dhcp 命令获取列表,然后使用正则表达式解析输出。还有DHCP Server Management API,但它仅适用于本机代码。我不知道有任何 powershell 模块或 .net 库可以做这种事情。我最终将 P/Invoke 包装器写入 DHCP 服务器管理 API。 【参考方案1】:

1.5 年后,我粘贴在原始答案中的 cmdlet 经历了如此多的更新,以至于它已经完全过时了。因此,我将code 和ReadMe 替换为最新版本的链接。

Join-Object

根据它们之间的相关属性组合两个对象列表。

说明 结合一个或多个对象的属性。它创建了一个可以保存为新对象或按原样使用的集合。对象连接是一种通过使用每个对象的共同值来组合来自一个(自连接)或多个对象列表的属性的方法。

主要特点

直观(类似 SQL)的语法 智能属性合并 用于更新、合并和特定连接类型的预定义连接命令 为(左)输入对象和输出对象定义明确的管道(正确使用时保留内存) 在大型对象列表上的执行速度比 Compare-Object 快约 40% 支持(自定义)对象、数据表和字典(例如哈希表)用于输入 智能属性和计算属性表达式 自定义关系表达式 易于安装(点源) 支持 PowerShell for Windows (5.1) 和 PowerShell Core

Join-Object cmdlet 显示以下代理命令及其自己的默认值(-JoinType-Property):

InnerJoin-Object(别名InnerJoinJoin),组合相关对象 LeftJoin-Object(别名LeftJoin),合并相关对象并添加剩余的左侧对象 RightJoin-Object(别名RightJoin),合并相关对象并添加其余正确对象 FullJoin-Object(别名FullJoin),合并相关对象,并添加剩下的左右对象 CrossJoin-Object(别名CrossJoin),将每个左侧对象与每个右侧对象组合在一起 Update-Object(别名Update),用相关的右对象更新左对象 Merge-Object(别名Merge),用相关的右侧对象更新左侧对象,并添加其余新的(不相关的)右侧对象

ReadMe

完整的自述文件(和源代码)可从 GitHub 获得:https://github.com/iRon7/Join-Object

安装

这个Join-Object cmdlet 有两个版本(两个版本提供相同的功能):

Join Module

Install-Module -Name JoinModule
Join Script

Install-Script -Name Join

(或将Join.psm1 模块重命名为Join.ps1 脚本文件) 并由dot sourcing调用脚本:

. .\Join.ps1

回答

回答问题中的实际示例:

$reservations |LeftJoin $leases -On IP

IP          MAC          Name
--          ---          ----
192.168.1.1 001D606839C2 Apple
192.168.1.2 00E018782BE1 Pear
192.168.1.3 0022192AF09C Banana
192.168.1.4 0013D4352A0D

性能

关于性能测量的一点点: PowerShell 管道旨在流式传输 对象(这可以保护内存),这意味着 输入对象的两个¹列表通常不(不应该)驻留在记忆。通常它们是从其他地方(即远程服务器、磁盘)检索的。此外,输出通常很重要,其中linq 的解决方案很快,但可能很容易让您在得出结论时走错路,因为linq 字面意思是延迟 em> 执行 (lazy evaluation),另请参阅:fastest way to get a uniquely index item from the property of an array。 换句话说,如果涉及到(测量)PowerShell 的性能,重要的是查看完整的端到端解决方案,它更可能看起来像:

 import-csv .\reservations.csv |LeftJoin (import-csv .\leases.csv) -On IP |Export-Csv .\results.csv

(1) 注意:不幸的是,没有简单的方法来构建两个并行输入流(参见:#15206 Deferred input pipelines)

(更多)示例

更多示例可以在相关的 *** 问题中找到:

Combining Multiple CSV Files Combine two CSVs - Add CSV as another Column CMD or Powershell command to combine (merge) corresponding lines from two files Can I use SQL commands (such as join) on objects in powershell, without any SQL server/database involved? CMD or Powershell command to combine (merge) corresponding lines from two files Compare Two CSVs, match the columns on 2 or more Columns, export specific columns from both csvs with powershell Merge two CSV files while adding new and overwriting existing entries Merging two CSVs and then re-ordering columns on output Merge two CSV files while adding new and overwriting existing entries Efficiently merge large object datasets having multiple matching keys Is there a PowerShell equivalent of paste (i.e., horizontal file concatenation)? combine (merge) corresponding lines from two files

在Join-Object test script。

如果您支持Add a Join-Object cmdlet to the standard PowerShell equipment (#14994)的建议,请给个?

【讨论】:

很棒的功能,但如果在 Powershell 模块中使用它,请小心。由于作用域的工作方式,$Left$Right 变量在-Merge 脚本块中不可用(模块的变量是模块私有的,因此脚本块看不到它们)。如果点源或直接在调用它的脚本中包含函数,这不是问题。 @Omni,我对将 cmdlet 放入模块进行了一些测试,但无法确认您的问题。然而,在修改 cmdlet 期间,我发现了一个相当大的错误,在 Unexpected results when reusing custom objects in the pipeline 中进行了描述。我创建了一个分支版本,粘贴在这个答案中。我怀疑这实际上是您遇到的问题(如果您能确认是否是这种情况,我将不胜感激)。【参考方案2】:

你可以像这样使用脚本块

$leases | select IP, NAME, @N='MAC';E=$tmp=$_.IP;($reservations| ? IP -eq $tmp).MAC

【讨论】:

【参考方案3】:

这是一个使用哈希表的简单示例。使用大数组,结果会更快。

$leases =
'IP,Name
192.168.1.1,Apple
192.168.1.2,Pear
192.168.1.3,Banana
192.168.1.99,FishyPC' | convertfrom-csv

$reservations =
'IP,MAC
192.168.1.1,001D606839C2
192.168.1.2,00E018782BE1
192.168.1.3,0022192AF09C
192.168.1.4,0013D4352A0D' | convertfrom-csv

$hashRes=@
foreach ($resRecord in $reservations) 
  $hashRes[$resRecord.IP] = $resRecord


$leases | foreach 
  $other = $hashRes[$_.IP]

  [pscustomobject]@IP=$_.IP
                   MAC=$other.MAC
                  Name=$_.name

IP           MAC          Name
--           ---          ----
192.168.1.1  001D606839C2 Apple
192.168.1.2  00E018782BE1 Pear
192.168.1.3  0022192AF09C Banana
192.168.1.99              FishyPC

【讨论】:

我很喜欢这个,因为它的适应性也很强。我可能会改变的一件事,除非有我遗漏的根本原因,否则您不需要创建 $other 并且由于 ForEach 循环比到 ForEach-Object 的管道更快,您可能想要更改如何处理 $leases 以匹配您之前的循环。像这样...Foreach ($leaseRecord in $leases)[PSCustomObject]@IP=$leaseRecord.IP; MAC=$hashRes[$leaseRecord.IP].MAC; Name=$leaseRecord.Name 在与外连接相同的循环中创建内连接:$reservations_outer=$reservations.clone(); $leases | foreach $lease=$_; $reservations_outer | where $_.IP -eq $lease.IP | add-member -notepropertymembers @name=$lease.name -passthru -outvariable reservations_inner【参考方案4】:

这也可以使用我的模块Join-Object来完成

Install-Module 'Join-Object'

Join-Object -Left $leases -Right $reservations -LeftJoinProperty 'IP' -RightJoinProperty 'IP'

关于性能,我针对 100k 行的样本数据进行了测试:

    @js2010 发布的哈希表示例运行时间为 8 秒。 Join-Object 由我在 14 秒内运行。 LeftJoin by @iRon 运行时间为 1 分 50 秒

【讨论】:

以上是关于在 Powershell 中,将两个表合并为一个的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 powershell 将两个 azure blob 合并为单个 blob

使用 PowerShell 将多个 CSV 文件合并为一个

如何使用 mongify 将两个表合并为一个

将两个表中的列合并为一个

如何使用 innerJoinAndSelect 将两个表合并为一个?

SQL 如何将一个表中的两条或多条拥有相同ID的记录合并为一条?