23

我对 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

有没有更好的方法来做到这一点?

4

4 回答 4

27

1.5 年后,我粘贴在原始答案中的 cmdlet 经历了如此多的更新,以至于它已经完全过时了。因此,我用最新版本的链接替换了代码自述文件。

连接对象

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

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

主要特点

  • 直观(类似 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),用相关的右对象更新左对象并添加其余新的(不相关的)右对象

自述文件

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

安装

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

Install-Module -Name JoinModule

Install-Script -Name Join

(或将Join.psm1模块重命名为Join.ps1脚本文件)并通过点源
调用脚本:

. .\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实际上会延迟执行(惰性评估),另请参阅:从数组属性中获取唯一索引项的最快方法
换句话说,如果涉及到 PowerShell 中的(测量)性能,那么查看完整的端到端解决方案很重要,它更可能看起来像:

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

(1) 注意:不幸的是,没有简单的方法来构建两个并行输入流(请参阅:#15206延迟输入管道

(更多)示例

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

并在Join-Object 测试脚本中。

如果您支持将Join-Object cmdlet 添加到标准 PowerShell 设备的建议,请提供 ( #14994)

于 2017-08-03T11:39:13.253 回答
3

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

Install-Module 'Join-Object'

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

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

  1. @js2010发布的 Hashtable 示例在 8 秒内运行。
  2. Join-Object由我在 14 秒内运行。
  3. LeftJoin@iRon在 1 分 50 秒内运行
于 2021-03-20T00:46:09.367 回答
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
于 2021-03-17T15:29:31.647 回答
1

您可以像这样使用脚本块

$leases | select IP, NAME, @{N='MAC';E={$tmp=$_.IP;($reservations| ? IP -eq $tmp).MAC}}
于 2020-08-13T05:52:20.417 回答