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

我对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地址的另一个映射arrays。 这代表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 

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

Lee Holmes写了一篇关于Join-Object函数的博客文章 。 太糟糕了,它还没有内置到PowerShell中。

Join.ps1

Join-Object (别名Join )函数将来自两个对象数组的列合并到一个新的对象数组中,该对象数组可以保存为一个表格( Export-CSV )或按照原样使用。

 Function Join-Object { [CmdletBinding()]Param ( [PSObject[]]$RightTable, [Alias("Using")]$On, $Merge = @{}, [Parameter(ValueFromPipeLine = $True)][Object[]]$LeftTable, [String]$Equals ) $Type = ($MyInvocation.InvocationName -Split "-")[0] $PipeLine = $Input | ForEach {$_}; If ($PipeLine) {$LeftTable = $PipeLine} If ($LeftTable -eq $Null) {If ($RightTable[0] -is [Array]) {$LeftTable = $RightTable[0]; $RightTable = $RightTable[-1]} Else {$LeftTable = $RightTable}} $DefaultMerge = If ($Merge -is [ScriptBlock]) {$Merge; $Merge = @{}} ElseIf ($Merge."") {$Merge.""} Else {{$Left.$_, $Right.$_}} If ($Equals) {$Merge.$Equals = {If ($Left.$Equals -ne $Null) {$Left.$Equals} Else {$Right.$Equals}}} ElseIf ($On -is [String] -or $On -is [Array]) {@($On) | ForEach {If (!$Merge.$_) {$Merge.$_ = {$Left.$_}}}} $LeftKeys = @($LeftTable[0].PSObject.Properties | ForEach {$_.Name}) $RightKeys = @($RightTable[0].PSObject.Properties | ForEach {$_.Name}) $Keys = $LeftKeys + $RightKeys | Select -Unique $Keys | Where {!$Merge.$_} | ForEach {$Merge.$_ = $DefaultMerge} $Properties = @{}; $Keys | ForEach {$Properties.$_ = $Null}; $Out = New-Object PSObject -Property $Properties $LeftOut = @($True) * @($LeftTable).Length; $RightOut = @($True) * @($RightTable).Length $NullObject = New-Object PSObject For ($LeftIndex = 0; $LeftIndex -lt $LeftOut.Length; $LeftIndex++) {$Left = $LeftTable[$LeftIndex] For ($RightIndex = 0; $RightIndex -lt $RightOut.Length; $RightIndex++) {$Right = $RightTable[$RightIndex] $Select = If ($On -is [String]) {If ($Equals) {$Left.$On -eq $Right.$Equals} Else {$Left.$On -eq $Right.$On}} ElseIf ($On -is [Array]) {($On | Where {!($Left.$_ -eq $Right.$_)}) -eq $Null} ElseIf ($On -is [ScriptBlock]) {&$On} Else {$True} If ($Select) {$Keys | ForEach {$Out.$_ = If ($LeftKeys -NotContains $_) {$Right.$_} ElseIf ($RightKeys -NotContains $_) {$Left.$_} Else {&$Merge.$_} }; $Out; $LeftOut[$LeftIndex], $RightOut[$RightIndex] = $Null } } } If ("LeftJoin", "FullJoin" -Contains $Type) { For ($LeftIndex = 0; $LeftIndex -lt $LeftOut.Length; $LeftIndex++) { If ($LeftOut[$LeftIndex]) {$Keys | ForEach {$Out.$_ = $LeftTable[$LeftIndex].$_}; $Out} } } If ("RightJoin", "FullJoin" -Contains $Type) { For ($RightIndex = 0; $RightIndex -lt $RightOut.Length; $RightIndex++) { If ($RightOut[$RightIndex]) {$Keys | ForEach {$Out.$_ = $RightTable[$RightIndex].$_}; $Out} } } }; Set-Alias Join Join-Object Set-Alias InnerJoin Join-Object; Set-Alias InnerJoin-Object Join-Object -Description "Returns records that have matching values in both tables" Set-Alias LeftJoin Join-Object; Set-Alias LeftJoin-Object Join-Object -Description "Returns all records from the left table and the matched records from the right table" Set-Alias RightJoin Join-Object; Set-Alias RightJoin-Object Join-Object -Description "Returns all records from the right table and the matched records from the left table" Set-Alias FullJoin Join-Object; Set-Alias FullJoin-Object Join-Object -Description "Returns all records when there is a match in either left or right table" 

句法

<Object[]> | InnerJoin|LeftJoin|RightJoin|FullJoin <Object[]> [-On <String>|<Array>|<ScriptBlock>] [-Merge <HashTable>|<ScriptBlock>] [-Eq <String>]

InnerJoin|LeftJoin|RightJoin|FullJoin <Object[]>,<Object[]> [-On <String>|<Array>|<ScriptBlock>] [-Merge <HashTable>|<ScriptBlock>] [-Eq <String>]

InnerJoin|LeftJoin|RightJoin|FullJoin -LeftTable <Object[]> -RightTable <Object[]> [-On <String>|<Array>|<ScriptBlock>] [-Merge <HashTable>|<ScriptBlock>] [-Eq <String>]

命令

Join-Object (别名Join )函数是一个具有几个别名的函数,它们连接两个表(每个表由PSCustomObjects组成 ),与相应的SQL Join指令类似。 默认的连接types是一个InnerJoin

  • InnerJoin-Object (别名InnerJoin
    返回两个表中具有匹配值的logging。

  • LeftJoin-Object (别名LeftJoin
    返回左表中的所有logging和右表中匹配的logging。

  • RightJoin-Object (别名RightJoin
    返回右表中的所有logging以及右表中匹配的logging。

  • FullJoin-Object (别名FullJoin
    当左侧或右侧表格匹配时,返回所有logging。

笔记

  1. 所有Join命令与PowerShell版本2和更高版本兼容。

参数

-LeftTable <Object[]>-RightTable <Object[]>

-LeftTableRightTable参数定义了要连接的左右表。 有三种可能的语法来提供表格:

  • 使用PowerShellpipe道: <LeftTable> | Join <RightTable> <LeftTable> | Join <RightTable>

  • 在第一个参数位置提供一个数组中的两个表(用逗号分隔): Join <LeftTable> , <RightTable>

  • 使用命名参数提供两个表: Join -Left <LeftTable> -Right <RightTable>

笔记

  1. 如果只提供一个表( Join <Table> ),则将在表上执行自我自连接 。

-On <String>|<Array>|<ScriptBlock>-Equals <String>

-On (别名Using )参数定义了指定如何连接表以及在(内部)结果集中包含哪些行的条件。 -On参数支持以下格式:

  • String -Equals <String>如果String -Equals <String>值是一个String并且提供了-Equals <String>参数, -Equals <String>值定义的左列中的属性要求等于右列定义的属性(内部)结果集中包含-equals值。

  • StringArray如果值是StringArray-On参数与SQL using子句类似。 这意味着所有列出的属性需要相等(在左侧和右侧)以包含在(内部)结果集中。 列出的属性将默认输出单个值(另请参阅-Expressions )。

  • ScriptBlock任何条件expression式,其中$Left定义左行, $Right定义右行。

笔记

  1. ScriptBlocktypes具有最多的比较可能性,但比其他types慢得多。

  2. 如果-On参数被忽略或来自未知types,则将执行交叉连接 。

-Merge <HashTable>|<ScriptBlock>

定义如何合并具有相同名称的特定列。 -Merge参数接受types:包含每个列的特定合并expression式的HashTable或包含默认合并expression式的ScriptBlock ,该expression式没有定义任何合并expression式。
在expression式中:

  • $_保存每个列名称。
  • $Left保留左行, $Right保存右行。
  • $Left.$_保存每个左值和$Right.$_保存每个正确的值。
  • $LeftIndex保存当前左行索引, $RightIndex保存当前右行索引。

笔记:

  1. 只有当左值( Left.$_ )和右值( Left.$_ )都存在(包括$Null值)时才执行expression式,否则只返回退出值。

  2. 如果没有为列定义expression式,则使用expression式{$Left.$_, $Right.$_} 。 这意味着这两个值都被赋值(在一个数组中)到当前属性。

  3. -Equals <String> ,- -Equals <String>-Equals <String> <Array>定义的列的expression式是: {$Left.$_} ,只能由散列表中定义的列专用expression式否决。 这意味着一个单独的值( $Left或者$Right不等于$Null )被分配给当前属性。

  4. 要使用特定于列的expression式定义默认expression式,请使用默认expression式的零长度键名称,例如-Merge @{"" = {$Left.$_}; "Column Name" = {$Right.$_}} -Merge @{"" = {$Left.$_}; "Column Name" = {$Right.$_}}

例子

鉴于以下表格:

  $Employee $Department +---------+---------+-------------+ +-------------+---------+---------+ | Name | Country | Department | | Name | Country | Manager | +---------+---------+-------------+ +-------------+---------+---------+ | Aerts | Belgium | Sales | | Engineering | Germany | Meyer | | Bauer | Germany | Engineering | | Marketing | England | Morris | | Cook | England | Sales | | Sales | France | Millet | | Duval | France | Engineering | +-------------+---------+---------+ | Evans | England | Marketing | | Fischer | Germany | Engineering | +---------+---------+-------------+ 
 PS C:\> # InnerJoin on Department = Name PS C:\> $Employee | InnerJoin $Department Department -eq Name | Format-Table Department Name Manager Country ---------- ---- ------- ------- Sales Aerts Millet {Belgium, France} Engineering Bauer Meyer {Germany, Germany} Sales Cook Millet {England, France} Engineering Duval Meyer {France, Germany} Marketing Evans Morris {England, England} Engineering Fischer Meyer {Germany, Germany} PS C:\> # LeftJoin using country (selecting Department.Name and Department.Country) PS C:\> $Employee | LeftJoin ($Department | Select Manager,Country) Country | Format-Table Department Name Manager Country ---------- ---- ------- ------- Engineering Bauer Meyer Germany Sales Cook Morris England Engineering Duval Millet France Marketing Evans Morris England Engineering Fischer Meyer Germany Sales Aerts Belgium PS C:\> # InnerJoin on Employee.Department = Department.Name and Employee.Country = Department.Country (returning only the left name and - country) PS C:\> $Employee | InnerJoin $Department {$Left.Department -eq $Right.Name -and $Left.Country -eq $Right.Country} {$Left.$_} Department Name Manager Country ---------- ---- ------- ------- Engineering Bauer Meyer Germany Marketing Evans Morris England Engineering Fischer Meyer Germany PS C:\> # Cross Join PS C:\> $Employee | InnerJoin $Department | Format-Table Department Name Manager Country ---------- ---- ------- ------- Sales {Aerts, Engineering} Meyer {Belgium, Germany} Sales {Aerts, Marketing} Morris {Belgium, England} Sales {Aerts, Sales} Millet {Belgium, France} Engineering {Bauer, Engineering} Meyer {Germany, Germany} Engineering {Bauer, Marketing} Morris {Germany, England} Engineering {Bauer, Sales} Millet {Germany, France} Sales {Cook, Engineering} Meyer {England, Germany} Sales {Cook, Marketing} Morris {England, England} Sales {Cook, Sales} Millet {England, France} Engineering {Duval, Engineering} Meyer {France, Germany} Engineering {Duval, Marketing} Morris {France, England} Engineering {Duval, Sales} Millet {France, France} Marketing {Evans, Engineering} Meyer {England, Germany} Marketing {Evans, Marketing} Morris {England, England} Marketing {Evans, Sales} Millet {England, France} Engineering {Fischer, Engineering} Meyer {Germany, Germany} Engineering {Fischer, Marketing} Morris {Germany, England} Engineering {Fischer, Sales} Millet {Germany, France} 

更新服务列表(replace名称上的现有服务,并添加新的服务)

 Import-CSV .\Svc.csv | LeftJoin (Get-Service) Name {$Right.$_} | Export-CSV .\Svc.csv 

更新进程列表并只插入具有更高CPU的进程

 Import-CSV .\CPU.csv | LeftJoin (Get-Process) ID {If ($Left.CPU -gt $Right.CPU) {$Left.$_} Else {$Right.$_}} | Export-CSV .\CPU.csv