在PowerShell中,将两个表连接成一个表的最佳方法是什么?

avwztpqn  于 2023-01-17  发布在  Shell
关注(0)|答案(5)|浏览(174)

我对PowerShell还是个新手,我想知道是否有人知道更好的方法来完成下面的示例问题。
我有一个从IP地址到主机名的Map数组。这表示一个活动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地址的Map数组,它代表了一个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地址和主机名的第三个Map数组,其思想是$reservations应该获得第三个字段“Name”,只要有匹配的“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

有没有更好的办法?

nnsrf1az

nnsrf1az1#

1.5年后,我粘贴在原始答案中的cmdlet经历了如此多的更新,以至于它已经完全过时,因此我用指向最新版本的链接替换了code and the ReadMe
∮ ∮ ∮ ∮
根据两个对象列表之间的相关特性合并它们。

    • 说明**

组合一个或多个对象的属性。它创建一个集,该集可另存为新对象或按原样使用。对象联接是一种通过使用一个(自联接)或多个对象列表的公用值来组合这些对象列表的属性的方法。

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

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

  • InnerJoin-Object(别名InnerJoinJoin),合并相关对象
  • LeftJoin-Object(Alias LeftJoin),合并相关对象并添加左侧的其余对象
  • RightJoin-Object(Alias RightJoin),合并相关对象并添加其余的右侧对象
  • FullJoin-Object(Alias FullJoin),合并相关对象并添加左侧和右侧的其余对象
  • CrossJoin-Object(Alias CrossJoin),将每个左侧对象与每个右侧对象合并
  • Update-Object(Alias Update),使用相关的右侧对象更新左侧对象
  • Merge-Object(Alias Merge),使用相关的右侧对象更新左侧对象,并添加其余的新(不相关)右侧对象

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

安装

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

Install-Module -Name JoinModule
Install-Script -Name Join

(or将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确实 * 延迟 * 了执行(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

(更多)示例

有关堆栈溢出问题的更多示例,请访问:

而在Join-Object test script中。
如果您支持对Add a Join-Object cmdlet to the standard PowerShell equipment ( #14994 )的建议,请给出a👍 if you support the proposal to Add a Join-Object cmdlet to the standard PowerShell equipment ( #14994 )

pepwfjgg

pepwfjgg2#

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

Install-Module 'Join-Object'

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

关于性能,我对100 k行的样本数据进行了测试:
1.由@js2010发布的哈希表示例运行时间为8秒。

  1. Join-Object由我运行在14秒。
  2. LeftJoin by @iRon在1分50秒内运行
hpxqektj

hpxqektj3#

下面是一个使用哈希表的简单例子,对于大数组,这个方法会更快。

$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
yh2wf1be

yh2wf1be4#

我发现合并两个Powershell对象的最简单方法是使用ConvertTo-Json和ConvertFrom-Json

    • 一个内衬基于手术Senario:**
$leases | foreach {(ConvertTo-Json $_) -replace ("}$", (ConvertTo-Json ($reservations | where IP -eq $_.IP | select * -ExcludeProperty IP)) -Replace "^{", ",")} 
| ConvertFrom-Json

结果:

IP          Name  Mac
--          ----  ---
192.168.1.1 Apple 001D606839C2
192.168.1.2 Pear  00E018782BE1

再举一个例子,让我们制作一对对象:

$object1 = [PSCustomObject]@{"A" = "1"; "B" = "2"}
$object2 = [PSCustomObject]@{"C" = "3"; "D" = "4"}
    • 使用Json通过替换左括号和右括号将它们合并在一起:**
(ConvertTo-Json $object1) -replace ("}$", $((ConvertTo-Json $object2) -Replace "^{", ",")) | ConvertFrom-Json

输出:

A B C D
- - - -
1 2 3 4
    • 使用一组对象的另一个示例:**
$mergedObjects = [PSCustomObject]@{"Object1" = $Object1; "Object2" = $Object2}
Object1     Object2
-------     -------
@{A=1; B=2} @{C=3; D=4}

可以在foreach中再次执行相同的操作:

$mergedObjects | foreach {(ConvertTo-Json $_.Object1) -replace ("}$", $((ConvertTo-Json $_.Object2) -Replace "^{", ",")) | ConvertFrom-Json}

输出:

A B C D
- - - -
1 2 3 4
vuv7lop3

vuv7lop35#

可以像这样使用脚本块

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

相关问题