在 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 CoreJoin-Object cmdlet 显示以下代理命令及其自己的默认值(-JoinType
和 -Property
):
InnerJoin-Object
(别名InnerJoin
或Join
),组合相关对象
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 有两个版本(两个版本提供相同的功能):
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 ofpaste
(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