PowerShell 中的内部联接(没有 SQL)

Inner Join in PowerShell (without SQL)

我们如何在 PowerShell 或 PowerCLI 中制作 Inner-Join 或其他东西 Cross-Join

尽管我是 PowerCLI/PowerShell 的新手,但我确实对它们有基本的了解,但实际上花了 2 天时间试图弄明白这一点,查阅了大量文档和博客都无济于事。

我真正想知道的是,如果在输入我的命令后

Get-Content File.txt 

并获得:

Output1 or Table1 is

Name: Abc
Group: Bad
Policy: Great

Name: redi
Group: Good 
Policy: MAD

etc. etc.

100 多个,显然不仅仅是名称、组、策略这 3 个元素。


Table2/Output2

Name: Abc
Limit: 10
used: 5

Name: redi
Limit: 20
used: 1

etc. etc.

100 多个。


以及另外 13 个这样的文本文件表,所有表的 "Name" 都是唯一的。

如何使用 Name 和所有其他元素在最后将其组合成一个输出?

我最明显的想法是类似于连接的东西,即使我必须一次做 1 个,但即便如此我也不知道该怎么做。

是否可以在 PowerShell 本身中执行此操作而无需进入 Python 或 SQL?

如果是,是否有一种方法可以在它为空的地方组合字段?

如果不清楚我希望得到什么类型的结果,它看起来类似于:

Name: Abc
Group: Bad
Policy: Great
Limit: 10
used: 5


Name: redi
Group: Good 
Policy: MAD
Limit: 20
used: 1

您可以使用简单的循环连接,如下所示:

$table1 = [pscustomobject]@{Name='Abc';Group='Bad';Policy='Great'},[pscustomobject]@{Name='redi';Group='Good ';Policy='MAD'}
$table2 = [pscustomobject]@{Name='Abc';Limit=10;used=5},[pscustomobject]@{Name='redi';Limit=20;used=1}

$table1 | % { 
                foreach ($t2 in $table2) {
                    if ($_.Name -eq $t2.Name) {
                        [pscustomobject]@{Name=$_.Name;Group=$_.Group;Policy=$_.Policy;Limit=$t2.Limit;Used=$t2.Used}
                    }
                }
            }

假设键的唯一性,您还可以使用更快的哈希表方法:

$hashed = $table1 | group Name -AsHashTable
$table2 | % {
    $matched = $hashed[$_.Name]
    if ($matched) {
        [pscustomobject]@{Name=$matched.Name;Group=$matched.Group;Policy=$matched.Policy;Limit=$_.Limit;Used=$_.Used}
    }
}

您也可以使用通用解决方案并将其包装在函数中。它按 属性 名称匹配记录:

function Join-Records($tab1, $tab2){
    $prop1 = $tab1 | select -First 1 | % {$_.PSObject.Properties.Name} #properties from t1
    $prop2 = $tab2 | select -First 1 | % {$_.PSObject.Properties.Name} #properties from t2
    $join = $prop1 | ? {$prop2 -Contains $_}
    $unique1 = $prop1 | ?{ $join -notcontains $_}
    $unique2 = $prop2 | ?{ $join -notcontains $_}


    if ($join) {
        $tab1 | % {
            $t1 = $_
            $tab2 | % {
                $t2 = $_
                foreach ($prop in $join) {
                    if (!$t1.$prop.Equals($t2.$prop)) { return; }
                }
                $result = @{}                
                $join | % { $result.Add($_,$t1.$_) }
                $unique1 | % { $result.Add($_,$t1.$_) }
                $unique2 | % { $result.Add($_,$t2.$_) }
                [PSCustomObject]$result
            }
        }
    }
}

$table1 = [pscustomobject]@{Name='Abc';Group='Bad';Policy='Great'},
    [pscustomobject]@{Name='redi';Group='Good ';Policy='MAD'},
    [pscustomobject]@{Name='Not joined';Group='Very bad';Policy='Great'}
$table2 = [pscustomobject]@{Name='Abc';Limit=10;used=5},
    [pscustomobject]@{Name='redi';Limit=20;used=1},
    [pscustomobject]@{Name='redi';Limit=20;used=2}

#name is only common property, records joined by name
Join-Records $table1 $table2

#example2
$test1 = [pscustomobject]@{A=1;B=1;C='R1'},
    [pscustomobject]@{A=1;B=2;C='R2'},
    [pscustomobject]@{A=2;B=2;C='R3'}

$test2 = [pscustomobject]@{A=1;B=1;D='R4'},
    [pscustomobject]@{A=3;B=2;D='R5'},
    [pscustomobject]@{A=4;B=2;D='R6'}

Join-Records $test1 $test2 #joined by two common columns - A and B

你也可以级联调用:

$test1 = [pscustomobject]@{A=1;B=1;C='R1'},
    [pscustomobject]@{A=1;B=2;C='R2'},
    [pscustomobject]@{A=2;B=2;C='R3'}

$test2 = [pscustomobject]@{A=1;B=1;D='R4'},
    [pscustomobject]@{A=3;B=2;D='R5'},
    [pscustomobject]@{A=4;B=2;D='R6'}

$test3 = [pscustomobject]@{B=1;E='R7'},
    [pscustomobject]@{B=2;E='R8'},
    [pscustomobject]@{B=3;E='R9'}

#first join by common A and B, then join result by common B
Join-Records (Join-Records $test1 $test2) $test3

基于你的两个 table。但是,您可能需要一个通用解决方案,您不必自己指定每个 属性 的名称。


我会将每个 table 组合成一个数组。使用 Group-Object cmdlet 对 Name 属性 上的 table 进行分组。迭代每个组并使用属性创建一个 PsObject:

$table1 = [PSCustomObject]@{ Name = 'Abc'; Group = 'Bad'; Policy = 'Great'}, [PSCustomObject]@{ Name = 'redi'; Group = 'Good'; Policy = 'MAD'}
$table2 = [PSCustomObject]@{ Name = 'Abc'; Limit = '10'; used = '5'}, [PSCustomObject]@{ Name = 'redi'; Limit = '20'; used = '1'}

$allTables = $table1 + $table2

$allTables | group Name | Foreach {
    $properties = @{}
    $_.Group | Foreach {
        $_.PsObject.Properties | Where Name -ne 'Name' | Foreach {
            $properties +=  @{
                "$($_.Name)" = "$($_.Value)"
            }
        }
    }
    $properties += @{Name = $_.Name}
    New-Object PSObject –Property $properties
}

输出:

Group  : Bad
Policy : Great
Name   : Abc
Limit  : 10
used   : 5

Group  : Good
Policy : MAD
Name   : redi
Limit  : 20
used   : 1

所以我找到了一个更合适的答案,它使用了下面定义的 join-Object 函数:

您可以在 https://github.com/RamblingCookieMonster/PowerShell/blob/master/Join-Object.ps1

访问它

我真正需要做的就是将我的输出定义为 $A 和 $B 以及 $C 等等,然后

$Join1=  Join-Object -Left $A -Right $B -LeftJoinProperty Name - RightJoinProperty Name

$Join2 然后是 3 等等,直到我全部完成

$Join2 = Join-Object -Left $Join1 -Right $C -LeftJoinProperty Name -RightJoinProperty Name

$Join3 = Join-Object -Left $Join2 -Right $D -LeftJoinProperty Name -RightJoinProperty Name

$Join4 = Join-Object -Left $Join3 -Right $E -LeftJoinProperty Name -RightJoinProperty Name

直到我全部完成

$Table1 | Join $Table2 -Using Name
$Table1 | Join $Table2 #Cross Join

参见:In Powershell, what's the best way to join two tables into one?