具有功能的管道的参数化

Parametrization of a pipe with a function

我发现 powershell 时有些高兴,有些沮丧,目前我在 powershell 能力方面碰壁了。

我想创建一个管道函数来汇总对象集合的成员类型。

没有函数,工作代码如下所示:

get-process |
foreach { get-member -inputobject $_ } |
foreach { "[$($_.membertype)]$($_.name)" } |
group-object |
sort count, name

现在,我目前对实现此功能的尝试是:

function get-membersummary {
  process {
    get-member -inputobject $_ |
    foreach { "[$($_.membertype)]$($_.name)" } |
    group-object |
    sort count, name
  }
}

应该这样使用:

&$anything | get-membersummary

明显的问题是"process"语句中的代码是针对每个元素调用的,也就是说对每个item进行了分组。我想要的是整两行分组的第一个结果。

是否可以在不使用会导致内存效率低下的数组变量的情况下在 powershell 中实现此目的?

另外,我很确定这个问题已经被问过了,但是我找不到合适的词来表达它。

您可以使用 SteppablePipeline,但是对于排序,您必须在开始排序之前收集整个输入,因此在这里使用数组几乎不会比您已经拥有的更多 "memory inefficiency"。

function get-membersummary {
    begin {
        $Pipeline={
            &foreach { get-member -inputobject $_ } |
            foreach { "[$($_.membertype)]$($_.name)" } |
            group-object|
            sort count, name
        }.GetSteppablePipeline()
        $Pipeline.Begin($MyInvocation.ExpectingInput,$ExecutionContext)
    }
    process {
        if($MyInvocation.ExpectingInput){
            $Pipeline.Process($_)
        }else{
            $Pipeline.Process()
        }
    }
    end {
        $Pipeline.End()
        $Pipeline.Dispose()
    }
}

接收到所有数据后才能进行分组排序,所以需要等到函数中的end {}块。两个例子:

#process using pipeline, but wait until end to group and sort
function get-membersummary {
  begin { $res = @() }

  process {
    $res += Get-Member -inputobject $_ |
    ForEach-Object { "[$($_.MemberType)]$($_.Name)" }
  }

  end {
    $res | Group-Object | Sort-Object Count, Name | Select-Object Count, Name
  }
}

#do everything after all objects have arrived
function get-membersummary2 {
  end {
    #in process { }, $input is the object in the pipeline. in end { } it is a collection of all the objects.
    $input | % {
        Get-Member -InputObject $_ |
        ForEach-Object { "[$($_.MemberType)]$($_.Name)" }
    } | Group-Object | Sort-Object Count, Name | Select-Object Count, Name
  }
}


Get-Process | get-membersummary | ft -AutoSize
#Get-Process | get-membersummary2 | ft -AutoSize

输出:

Count Name
----- ----
   75 [AliasProperty]Handles
   75 [AliasProperty]Name
   75 [AliasProperty]NPM
   75 [AliasProperty]PM
   75 [AliasProperty]VM
   75 [AliasProperty]WS
   75 [Event]Disposed
   75 [Event]ErrorDataReceived
   75 [Event]Exited
   75 [Event]OutputDataReceived
....

一般来说,您应该避免在函数中使用 Group-ObjectSort-Object,因为它们会破坏管道的流程。 Select-Object (我这次添加的)也应该避免,因为它会破坏原始对象。我理解在这种情况下的选择,但要注意不要过度使用它们。您可以编写一个函数或过滤器来处理对象,然后在需要时手动调用 groupsort,例如:

filter get-membersummary3 {
  $_ |
  Get-Member |
  ForEach-Object { "[$($_.MemberType)]$($_.Name)" }
}


Get-Process | get-membersummary3 | Group-Object | Sort-Object Count, Name | Select-Object Count, Name