首页 > 解决方案 > 电源外壳。管道命令和使用 Foreach-Object 之间的区别

问题描述

抱歉,如果这个问题已经得到解答,我可以找到类似的问题,但不是我需要问的确切问题。

让我们举两个例子:

 1. Get-Process -name msedge,putty | Stop-Process
 2. Get-Process -name msedge,putty | Foreach-Object {Stop-Process $_}

两者都在做同样的操作。每种方法使用的方法如何?它们是否相同,因为第一个示例只是Foreach-Object为了代码可读性/美观而省略了构造?

标签: windowspowershellpipelineforeach-object

解决方案


第一个示例需要 Cmdlet 支持通过管道绑定相关参数。在您的情况下Stop-Process,将 Process 对象从管道绑定到它的-InputObject参数。

您可以检查使用 get-help stop-process -Parameter *并查看哪些参数具有“接受管道输入?” 设置为真。

如果 Cmdlet 不支持相关参数值的绑定,您可以将其包裹ForEach-Object起来,就像您在第二个示例中所做的那样。这样,您可以使用自动变量$_将当前管道对象(或您从中派生的信息)“手动”绑定到相应的参数。

如果 Cmdlet 支持从管道绑定参数值,您应该使用什么方法?不幸的是,这取决于。可以编写行为不同的 Cmdlet,具体取决于参数值的绑定方式。让我来说明这一点:

function Test-BindingFoo {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline)]
        [string[]]
        $InputParameter
    )
    
    begin {
        Write-Host "[BEGIN]"
    }
    
    process {
        foreach ($value in $InputParameter) {
            Write-Host "The current value is: $value"
        }
    }
    
    end {
        Write-Host "[END]"
    }
}

如果您使用管道绑定执行此 Cmdlet,则函数的 Begin 块将仅执行一次:

❯ "foo1", "foo2" | Test-BindingFoo
[BEGIN]
The current value is: foo1
The current value is: foo2
[END]

如果您使用ForEach-ObjectBegin 块,则每次对象通过管道时都会执行:

❯ "foo1", "foo2" | ForEach-Object { Test-BindingFoo $_ }
[BEGIN]
The current value is: foo1
[END]
[BEGIN]
The current value is: foo2
[END]

在实施良好的 Cmdlet 中,这里的差异应该无关紧要。但是我发现以我们在此处讨论的方式传入参数时,了解 Cmdlet 内部发生的情况很有用。


推荐阅读