代码之家  ›  专栏  ›  技术社区  ›  scobi

如何使用绑定参数“别名”,而不中断PowerShell中的管道?

  •  2
  • scobi  · 技术社区  · 6 年前

    我发现自己经常追捕 |select -first 10 在命令末尾,我想把它缩短到 |s10 , |s50 以及其他一些变体。所以我想做相当于 set-alias s10 select-object -first 10 .

    使用绑定参数“别名”的标准方法是编写一个函数并将@args与额外的参数一起转发。但是如果我写一个函数 $input 通过 select-object ,我失去了流媒体。

    我可以编写一个begin/process/end函数,但我不知道是否可以/如何将这些函数转发给select对象中的等价函数。我可以编写自己的select对象的begin/process/end实现,它只实现第一个行为,但这是错误的…

    (我的回退是添加一个标签完成来扩展s10,但是我真的想学习如何实现一个适当的函数。)

    如何实现转发到 选择对象 我希望添加一个参数,但不中断管道连接?

    2 回复  |  直到 6 年前
        1
  •  1
  •   Gert Jan Kraaijeveld    6 年前

    我找到了这个来源 https://blogs.technet.microsoft.com/heyscriptingguy/2011/03/01/proxy-functions-spice-up-your-powershell-core-cmdlets/

    在本例中,select string-first 10的快捷方式/别名可归结为:

    $metadata = New-Object System.Management.Automation.CommandMetaData (Get-Command Select-Object)
    [System.Management.Automation.ProxyCommand]::Create($MetaData) | Out-File -FilePath prxyfunctions.psm1
    

    打开prxyfunctions.psm1模块文件,并在名为s10的新函数中包装完整内容。

    function S10 {
        [CmdletBinding(DefaultParameterSetName = 'DefaultParameter', HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=113387', RemotingCapability = 'None')]
        param(
    
        <abbreviated...>
    
    .ForwardHelpTargetName Microsoft.PowerShell.Utility\Select-Object
    .ForwardHelpCategory Cmdlet
    
    #>
    }
    

    然后在begin部分添加一条语句 $PSBoundParameters.Add('First','10') 像下面一样。

    begin {
            try {
                $outBuffer = $null
                if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                    $PSBoundParameters['OutBuffer'] = 1
                }
                $PSBoundParameters.Add('First','10')
                $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Select-Object', [System.Management.Automation.CommandTypes]::Cmdlet)
                $scriptCmd = {& $wrappedCmd @PSBoundParameters }
                $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
                $steppablePipeline.Begin($PSCmdlet)
            }
            catch {
                throw
            }
        }
    

    就是这样。保存文件,导入模块,键入一个简短的命令,例如 gci c:\|s10 只得到10个结果。 如果你真的想让事情防错,就需要更多的编码。如果 S10 -First 2 如果你使用了它,你会得到一个很好的错误。


    编辑 回应@petseral的有用评论

    管道中的某些Cmdlet可能无法处理代理函数,例如sort对象。比较这两行的输出

    -join (20..1 | Select -First 10 | Sort)
    11121314151617181920
    
    -join (20..1 | S10 | Sort)
    <nothing>
    
    -join (20..1 | S10 -Wait | Sort)
    11121314151617181920
    

    可以通过在命令行上使用-wait参数来解决这个问题。或者在proxy函数中对wait参数进行编码 $PSBoundParameters.Add('Wait',$true)

    在处理大型集合时,这是很不幸的,因为它禁用了选择对象功能,该功能在x元素之后停止管道,从而导致更多的处理和更长的等待时间。

        2
  •  0
  •   Leon Evans    6 年前

    在生产脚本中永远不要使用别名是我要说的(并且被认为是最佳实践)。如果它有一点测试代码或者其他人永远不会使用的快速而肮脏的东西,那就足够公平了,但决不会出现在生产脚本中。别名可以被删除、更改以运行其他命令,并且由于它们是特定于用户的,因此会给您带来意想不到的结果。