ForEach-Object是对管道中的单个对象还是对象集合进行操作?

时间:2017-11-18 10:52:53

标签: powershell pipeline

我在掌握PowerShell管道的工作方式时遇到了麻烦,我发现很多问题都归因于ForEach-Object。在我使用的其他语言中,foreach对一个集合进行操作,依次遍历集合的每个元素。我假设ForEach-Object在PowerShell管道中使用时也会这样做。但是,我读到的有关管道的所有内容都表明集合的每个元素都是分别通过管道传递的,并且重复调用下游cmdlet,分别对每个元素进行操作,而不是整个集合。

ForEach-Object对集合中的单个元素进行操作,而不是对整个集合进行操作吗?以不同的方式查看它,管道操作符是否将整个集合传递给ForEach-Object,然后迭代它,或管道对象是否在集合上迭代并将每个元素分别传递给ForEach-Object

3 个答案:

答案 0 :(得分:7)

ForEach-Object cmdlet - 与foreach 语句不同 - 本身执行 no 枚举

相反,它对通过管道传递的每个项目进行操作(在接收第一个项目之后以及接收到最后一个项目之后,还可以选择执行代码,如果有的话)。

因此,如果管道提供枚举(默认情况下),并且ForEach-Object只是调用收到的每件商品的脚本块。

以下示例说明了这一点:

# Let the pipeline enumerate the elements of an array:
> 1, 2 | ForEach-Object { "item: [$_]; count: $($_.Count)" }
item: [1]; count: 1
item: [2]; count: 1

# Send the array *as a whole* through the pipeline (PSv4+)
> Write-Output -NoEnumerate 1, 2 | ForEach-Object { "item: [$_]; count: $($_.Count)" }
item: [1 2]; count: 2

请注意脚本/函数/ cmdlet可以选择是否应该枚举他们写入输出流(管道)的集合或作为整体发送(作为单个)对象)。

在PowerShell代码(脚本或函数,无论是否为高级(cmdlet))中,枚举是默认值,但您可以选择 Write-Output -NoEnumerate ; {{1}在PSv4中引入了switch;在此之前,您必须使用 -NoEnumerate ,这只适用于高级脚本/函数。

另请注意将命令嵌入表达式,方法是将其封装在$PSCmdlet.WriteObject()强制枚举中:

(...)

答案 1 :(得分:2)

ForEach-Object遍历集合中的每个项目。当它完成在当前项目上执行它的scriptblock时,它会沿着管道发送到下一个命令,然后该命令可以立即开始处理它(当ForEach-Object处理下一个项目时,如果有的话)。

您可以在以下示例中看到此操作:

Get-Process | ForEach-Object { Start-Sleep 1; $_ } | Format-Table

Get-Process cmdlet获取进程列表,并立即将每个进程立即发送到ForEach-ObjectForEach-Object等待1秒,然后输出当前管道元素$_。这是由Format-Table收到的,它将其作为表格输出。您可以看到它在输出到屏幕之前不会等到所有进程都被处理完毕。

答案 2 :(得分:2)

答案是......两者都有。

支持流水线操作(高级功能)的PowerShell功能将处理单独通过管道的每个项目。它还可以定义一个beginend块,它在流水线阶段只执行一次。换句话说,基本结构是:

function Do-Stuff {
    begin {
         write-output "This will be done once, at the beginning"
    }
    process {
      Write-output "This will be done for each item"
    }
    end {
        Write-output "This will be done once, at the end"
    }
}

1..3 | foreach-Object {Do-Stuff $_}的输出将为:

This will be done once, at the beginning
This will be done for each item
This will be done for each item
This will be done for each item
This will be done once, at the end

因为Do-Stuff正在写入输出流,如果在此Foreach-Object之后还有其他管道阶段,则每个对象输出将依次传递到下一个阶段。如果没有任何其他阶段或其他任何东西来捕获输出,输出流将被写入控制台。

例如:

$verbosepreference = "continue";
[int]1..3|foreach-object {write-output $_; write-verbose ($_*-1)}|foreach-object {$_*$_;write-verbose $_} 

提供以下输出:

1
VERBOSE: 1
VERBOSE: -1
4
VERBOSE: 2
VERBOSE: -2
9
VERBOSE: 3
VERBOSE: -3

-X最后输出到Verbose流(对于每个项目),因为输出已传递到管道的下一个阶段并在之前处理 {{em> 1}} scriptblock已执行。