对管道中的Powershell对象进行分组和排名

时间:2017-12-10 17:15:16

标签: powershell powershell-v2.0 powershell-v4.0

我正在尝试在powerhell管道中对组中的对象进行排名。

Get-ChildItem "*.csv" | 
Select-object fullname, Length, @{N = "Environment"; E = {
    if ($_.fullname -like "*Dev*") {
        "DEV"
    }
    elseif ($_.fullname -like "*PROD*") {
        "PROD"
    }
    elseif ($_.fullname -like "*UAT*") {
        "UAT"
    }
}} | Sort-Object -Property Environment, Length 

我想在组(环境和长度)的基础上在管道中添加具有以下值的Rank属性。在SQL ranking function

这样的PowerShell中是否有任何内置功能
FullName                               Length Environment Rank 
--------                               ------ ----------- ----
C:\Temp\ReportDEV-20171210_210653.csv    3065 DEV         1
C:\Temp\ReportDEV-20171210_211041.csv    9116 DEV         2
C:\Temp\ReportDEV-20171210_190100.csv   76286 DEV         3
C:\Temp\ReportDEV-20171210_200229.csv  511546 DEV         4
C:\Temp\ReportPROD-20171210_210349.csv   2835 PROD        1
C:\Temp\ReportPROD-20171210_210754.csv   8897 PROD        2
C:\Temp\ReportPROD-20171210_184729.csv  43850 PROD        3
C:\Temp\ReportPROD-20171210_191133.csv 213202 PROD        4
C:\Temp\ReportUAT-20171210_210554.csv    3065 UAT         1
C:\Temp\ReportUAT-20171210_210920.csv    9116 UAT         2
C:\Temp\ReportUAT-20171210_185308.csv   57244 UAT         3
C:\Temp\ReportUAT-20171210_193211.csv  306154 UAT         4

2 个答案:

答案 0 :(得分:1)

您可以使用for循环将排名简单地添加到输出对象。

ForEach-Object在第一个参数中接受脚本块作为开始脚本。我用它来声明计数器然后是第二个参数,它是用于处理脚本的,除了创建所需的输出对象之外,我还增加了计数器。

示例1 - 行的行号:

Get-ChildItem "C:\SomeDirectory" -Recurse -File | Sort-Object Name |
    ForEach-Object {$i = 1} {
        New-Object psObject -Property @{Name= $_.Name; Rank= $i++;}
}

结果

Name           Rank
----           ----
Product1-1.txt    1
Product1-2.txt    2
Product1-3.txt    3
Product2-1.txt    4
Product2-2.txt    5

示例2 - 组中行的行号:

Get-ChildItem "C:\SomeDirectory" -Recurse -File | Group-Object DirectoryName | 
    ForEach-Object {
        $_.Group | ForEach-Object {$i = 1} {
            New-Object psObject -Property @{
                 GroupName= $_.Directory.Name; Name= $_.Name;Rank= $i++;
            }
        }
    }

结果

GroupName Name           Rank
--------- ----           ----
Category1 Product1-1.txt    1
Category1 Product1-2.txt    2
Category1 Product1-3.txt    3
Category2 Product2-1.txt    1
Category2 Product2-2.txt    2

答案 1 :(得分:1)

不幸的是,PowerShell 提供开箱即用的排名功能,因此需要额外的工作:

$rank = 0
$prevEnv = ''
Get-ChildItem *.csv | Select-Object FullName, Length, @{ 
    n='Environment'; e={ $_.Name -replace '.*(DEV|PROD|UAT).*', '$1' } } |
  Sort-Object Environment, Length | Select-Object *, @{
      n='Rank'; e={
        ++$rank
        if ($prevEnv -ne $_.Environment) { 
          $rank = 1
          Set-Variable -Scope 1 prevEnv $_.Environment
        }
        Set-Variable -Scope 1 rank $rank
        $rank
    }
  }

注意将输入文件名映射到.Environment属性中的环境名称的简化方法,使用带有正则表达式的-replace和捕获组从文件名中提取感兴趣的令牌

  • 通过.Environment添加Select-Object属性后,对象首先按.Environment排序,然后按.Length排序(文件大小,以字节为单位)。

  • 然后通过另一个.Rank调用为结果对象提供Select-Object属性,其值为基于1的序列号,每当新的{{1遇到值,实际上相当于每个环境的文件大小排名。

  • 请注意.Environment需要修改Set-Variable$rank变量,因为这些变量必须在整个管道范围内按顺序维护为了使它们能够在连续的$prevEnv调用中持续存在(相反,例如,在分配给键Select-Object的脚本块中,本地e变量将仅限于该脚本块)。

或者,使用$rank(更简洁,但效率更低):

Group-Object
  • Get-ChildItem *.csv | Select-Object FullName, Length, @{ n='Environment'; e={ $_.Name -replace '.*(DEV|PROD|UAT).*', '$1' } } | Group-Object Environment | ForEach-Object { $rank = 0 $_.Group | Sort-Object Length | Select-Object *, @{ n='Rank'; e={ Set-Variable -Scope 1 rank ($rank+1); $rank } } } 按新添加的Group-Object Environment属性对输入对象进行分组。

  • 然后
  • .Environment遍历所有结果组。

    • 每个组的ForEach-Object)成员都是通过$_属性枚举的,并按.Group排序。
    • 第二个.Length调用然后循环遍历长度排序的组成员,并生成具有Select-Object属性的扩充输出对象,该属性反映基于文件的基于1的组相对排名尺寸(长度)按升序排列。

    • 请注意.Rank需要增加Set-Variable变量,因为该变量必须保持在$rank主体的级别,以便在连续的数据中存在ForEach-Object调用(在分配给键Select-Object的脚本块内部,本地e变量仅限于该脚本块)。