步进式管道有什么好处

iRo*_*Ron 4 powershell pipeline

在我们公司,我是个新人,他们几乎为每个 Native PowerShell cmdlet 都有一个包装器,主要是为了添加更多日志记录和错误处理。我试图反驳这一点,并参考内部 PowerShell 功能来创建一个代理命令,例如:

$GCI = Get-Command Get-ChildItem
[System.Management.Automation.ProxyCommand]::Create($GCI)
Run Code Online (Sandbox Code Playgroud)

但我在这里缺乏一些知识。
SteppablePipeline 和使用本机 PowerShell 语法之间有什么区别(如果有)。
换句话说,在Process块中,有什么区别:

$steppablePipeline.Process($_)
Run Code Online (Sandbox Code Playgroud)

并使用本机 PowerShell 语法:

$_ |Microsoft.PowerShell.Management\Get-ChildItem # In this example
Run Code Online (Sandbox Code Playgroud)

我知道我正在寻找一般信息,但在我看来,几乎没有任何关于例如ScriptBlock.GetSteppablePipeline方法的信息

mkl*_*nt0 5

\n

这篇 2009 年的著名博客文章介绍了代理函数(包装函数),解释了需要可步进管道来实现它们;以下引用表明(但没有明确说明)它们可能是为此目的而创建的:

\n
\n

特别是,您希望能够控制调用命令 \xe2\x80\x93 的执行,以控制 \xe2\x80\x99 何时 BEGINPROCESS()、PROCESSRECORD()、ENDPROCESS() 等方法被称为

\n
\n

简而言之,代理函数通过可步进管道,允许您以内存高效、流式传输的方式将大部分实现委托给另一个cmdlet ,从而实现 cmdlet(高级功能) 。

\n

具体来说,可步进管道允许您将代理函数的实现委托给脚本块,该脚本块的生命周期在初始化(块)、每个对象管道输入处理(块)方面与代理函数本身保持同步,和终止(块),这意味着包装的 cmdlet 的单个实例实际上直接连接到与代理函数本身相同的管道beginprocessend

\n

相反,这意味着:在以下场景中,您并不严格需要代理函数来编写包装函数:

\n
    \n
  • 如果您的包装函数不需要支持管道输入。

    \n
  • \n
  • 如果您不介意先收集所有管道输入,然后再将其全部传递给包装函数的块中的包装 cmdlet end,这意味着您将放弃处理

    \n
      \n
    • 如果您为块中的每个输入对象调用包装的 cmdlet,则也可能获得流处理,这样做:\nprocess
        \n
      • 效率低下(在嵌套管道中的每次迭代中完全调用包装的 cmdlet)
      • \n
      • 不适用于需要对所有输入作为一个整体进行操作的 cmdlet ,例如Format-*cmdlet 或聚合 cmdlet,例如Sort-ObjectGroup-Object
      • \n
      \n
    • \n
    \n
  • \n
\n
\n

以下是包装函数around的三种不同实现Select-String,它仅将每个匹配行的匹配部分报告为字符串,以说明权衡:

\n
    \n
  • Select-MatchProxy是一个适当的代理函数,即它Select-String通过可步进管道进行调用,这相当于仅涉及的单个调用实例化的流处理Select-String

    \n\n
  • \n
  • Select-MatchSimpleSelect-String每个块中调用一个新的实例process,这也相当于流式处理,但性能较差;如上所述,这种实现方法并不总是可行,具体取决于所包装的 cmdlet。

    \n
  • \n
  • Select-MatchCollect预先收集所有管道输入,然后将其传递到块Select-Stringend,这会放弃流处理并且占用大量内存;然而,就运行时而言,它实际上比代理功能表现得稍好一些。

    \n
  • \n
\n
function Select-MatchProxy {\n  [CmdletBinding(PositionalBinding=$false)]\n  param(\n    [Parameter(Mandatory, ValueFromPipeline)]\n    $InputObject,\n    [Parameter(Mandatory, Position=0)]\n    [string] $Pattern\n  )\n  begin {\n    $steppablePipeline = { \n       Select-String -Pattern $Pattern | ForEach-Object { $_.Matches.Value }\n     }.GetSteppablePipeline($myInvocation.CommandOrigin)\n    $steppablePipeline.Begin($PSCmdlet)\n  }\n  process {\n    $steppablePipeline.Process($InputObject)\n  }\n  end {\n    $steppablePipeline.End()\n  }\n}\n\nfunction Select-MatchSimple {\n  [CmdletBinding(PositionalBinding=$false)]\n  param(\n    [Parameter(Mandatory, ValueFromPipeline)]\n    $InputObject,\n    [Parameter(Mandatory, Position=0)]\n    [string] $Pattern\n  )\n  process {\n    Select-String -InputObject $InputObject -Pattern $Pattern |\n      ForEach-Object {\n        $_.Matches.Value\n      }\n  }\n}\n\nfunction Select-MatchCollect {\n  [CmdletBinding(PositionalBinding=$false)]\n  param(\n    [Parameter(Mandatory, ValueFromPipeline)]\n    $InputObject,\n    [Parameter(Mandatory, Position=0)]\n    [string] $Pattern\n  )\n  begin {\n    $l = [System.Collections.Generic.List[object]]::new()\n  }\n  process {\n    $l.Add($InputObject)\n  }\n  end {\n    $l | Select-String -Pattern $Pattern | ForEach-Object { $_.Matches.Value }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

比较运行时间,可以使用以下代码:

\n
# Sample input array of 100,000 strings.\n$array = (\'foo\', \'bar\') * 50000\n# Time 15 runs of each function, and report the average.\nTime-Command { $array | Select-MatchProxy   \'o+\' }, \n             { $array | Select-MatchSimple  \'o+\' }, \n             { $array | Select-MatchCollect \'o+\' }\n
Run Code Online (Sandbox Code Playgroud)\n

运行 PowerShell Core 7.3.0-preview.6 的 macOS 12.4 M1 Mac 的示例计时,可了解相对性能:

\n
Factor Secs (15-run avg.) Command                           TimeSpan\n------ ------------------ -------                           --------\n1.00   0.916              $array | Select-MatchCollect \'o+\' 00:00:00.9162298\n1.12   1.025              $array | Select-MatchProxy   \'o+\' 00:00:01.0254835\n5.38   4.930              $array | Select-MatchSimple  \'o+\' 00:00:04.9298495\n
Run Code Online (Sandbox Code Playgroud)\n

上面使用了这个 GistTime-Command中的函数。

\n
    \n
  • 假设您已经查看了链接的 Gist 的源代码以确保它是安全的(我个人可以向您保证,但您应该始终检查),您可以直接安装它,如下所示:

    \n
    irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n

  • 在一些示例中,我看到“$myInspiration.CommandOrigin”被传递给“GetSteppablePipeline()”。您能解释一下何时需要这样做吗?它似乎与运行空间有关,但我找不到任何解释其行为如何根据参数改变。 (2认同)