2017-11-18 52 views
2

我很难理解PowerShell管道是如何工作的,我意识到很多问题都是由于ForEach-Object造成的。在我用过的其他语言中,foreach对集合进行操作,并依次遍历集合中的每个元素。我假设ForEach-Object在PowerShell管道中使用时,会执行相同的操作。但是,我读到的关于管道的所有信息都表明,集合中的每个元素分别通过管道传递,并且下游的cmdlet被重复调用,分别在每个元素上运行,而不是整个集合。ForEach-Object是否对管道中的单个对象或对象集合进行操作?

因此,ForEach-Object是对集合中的单个元素进行操作,而不是对集合进行整体操作?用不同的方式来看,管道操作员是否将整个集合传递给ForEach-Object,然后对其进行迭代,或者管道对象是否迭代集合并将每个元素单独传递给ForEach-Object

回答

4

ForEach-Object cmdlet的 - 不像foreach声明 - 本身执行没有枚举

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

因此,可以说是很差命名,因为它是管道提供枚举(默认),并且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代码(脚本或功能,无论是先进的(小命令状)与否,枚举是默认的,但你可以Write-Output -NoEnumerate退出;在-NoEnumerate开关是PSv4介绍,在此之前,你不得不使用$PSCmdlet.WriteObject(),这是仅适用于先进脚本/功能。

还要注意,表达嵌入的命令通过在(...)力枚举包围它

# Send array as a whole. 
> Write-Output -NoEnumerate 1, 2 | Measure-Object 

Count: 1 
... 

# Converting the Write-Output -NoEnumerate command to an expression 
# by enclosing it in in (...) forces enumeration 
> (Write-Output -NoEnumerate 1, 2) | Measure-Object 

Count: 2 
... 
1

ForEach-Object迭代集合中的每个项目。当它完成对当前项目执行的脚本块时,它会沿管道发送到下一个可以立即开始处理的命令(而ForEach-Object正在处理下一个项目,如果有的话)。

您可以在下面的例子中看到这些内容起作用:

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

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

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输出到最后的冗长流(每个项目),因为输出被传递到下一阶段在之前的管道和foreach-object脚本块中的下一个语句被执行。

+1

快速拨出:'[INT] 1..3'相同'([INT] 1).. 3',这是因为刚'1..3'相同,假设范围运算符表达式的结果是_always_ a(带有[[int]'元素的'[System.Object []]')数组。不管怎么说,像'1'和'3'这样的数字文字都是'[int]'-type,但即使它们不是,PowerShell也会简单地强制它们;例如“1”,“3”将产生相同的结果。 – mklement0

相关问题