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?
我们如何在 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 组合成一个数组。使用 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?