PowerShell:ForEach-Object 的神秘 -RemainingScripts 参数 [英] PowerShell: the mysterious -RemainingScripts parameter of ForEach-Object

查看:90
本文介绍了PowerShell:ForEach-Object 的神秘 -RemainingScripts 参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

小问题:有人有关于 ForEach-Object 的 -RemainingScripts 参数的详细信息吗?

Short question: anyone has detailed information on -RemainingScripts parameter of ForEach-Object?

长问题:

我从上周开始学习 PowerShell,我正在浏览每个 Cmdlet 以了解更多详细信息.根据公开文档,我们知道 ForEach-Object 可以有 Begin-Process-End 块,如下所示:

I just started learning PowerShell from last week and I'm going through each Cmdlets to learn more details. Based on public documentation, we know ForEach-Object can have Begin-Process-End blocks, like this:

Get-ChildItem | foreach -Begin { "block1";
    $fileCount = $directoryCount = 0} -Process { "block2";
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}} -End {
    "block3"; "$directoryCount directories and $fileCount files"}

预期结果:"block1" 和 "block3" 1 次,传入的每个 item 都重复 "block2",并且目录计数/文件计数都正确.到目前为止一切顺利.

Result is expected: 1 time for "block1" and "block3", "block2" is repeated for each item passed in, and dir count/file count are all correct. So far so good.

现在,有趣的是,以下命令也有效并给出完全相同的结果:

Now, what's interesting is, the following command also works and gives exactly the same result:

Get-ChildItem | foreach { "block1"
    $fileCount = $directoryCount = 0}{ "block2";
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}{
    "block3"; "$directoryCount directories and $fileCount files"}

只传递了 3 个 ScriptBlocks 给 foreach.根据手册,第一个转到 -Process(位置 1).但是剩下的两个呢?根据手册,位置2"没有参数.于是我转向 Trace-Command,发现后面的 2 个脚本块实际上是把 RemainingScripts 变成了IList with 2 elements".

Just 3 ScriptBlocks passed to foreach. Based on manual, the first one goes to -Process (Position 1). But how about the remaining 2? According to manual, there's not a parameter with "Position 2". So I turned to Trace-Command, and found that the later 2 script blocks were actually to RemainingScripts as "IList with 2 elements".

BIND arg [$fileCount = $directoryCount = 0] to parameter [Process]
BIND arg [System.Management.Automation.ScriptBlock[]] to param [Process] SUCCESSFUL
BIND arg [System.Collections.ArrayList] to parameter [RemainingScripts]
BIND arg [System.Management.Automation.ScriptBlock[]] to param [RemainingScripts] SUCCESSFUL

所以如果我把命令改成这样:

So If I change the command to this:

# No difference with/without the comma "," between the last 2 blocks
Get-ChildItem | foreach -Process { "block1"
    $fileCount = $directoryCount = 0} -RemainingScripts { "block2";
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}},{
    "block3"; "$directoryCount directories and $fileCount files"}

结果还是一样.

如您所见,所有 3 个命令都给出相同的结果.这提出了一个有趣的问题:后面的两个命令(隐式)都指定了 -Process,但是 ForEach-Object 出人意料地将 -Process 的参数用作-Begin"!(脚本块被执行一开始就一次).

So as you noticed, all 3 commands give the same result. This raises the interesting question: both of the later two commands (implicitly) specified -Process, however ForEach-Object surprisingly ends up using the argument of -Process as "-Begin"! (script block is executed once at the beginning).

这个实验表明:

  1. -RemainingScripts 参数将获取所有未绑定的 ScriptBlocks
  2. 当传入3个block时,虽然第一个到了-Process,但后面实际上是作为Begin"使用的,剩下的2个变成了Process"和End"

不过,以上所有内容都只是我的疯狂猜测.我没有找到支持我猜测的文档

Still, all above are just my wild guess. I didn't find documentation to support my guess

所以,最后我们回到我的简短问题:)有人有 ForEach-Object 的 -RemainingScripts 参数的详细信息吗?

So, finally we go back to my short question :) Anyone has detailed information on -RemainingScripts parameter of ForEach-Object?

谢谢.

推荐答案

我做了更多的研究,现在有信心回答传入多个 ScriptBlock 时 -RemainingScripts 参数的行为.

I did more research and now feel confident to answer the behavior of -RemainingScripts parameter when multiple ScriptBlocks are passed in.

如果您运行以下命令并仔细检查结果,您将找到模式.这不是很简单,但仍然不难弄清楚.

If you run the following commands and inspect the result carefully, you will find the pattern. It's not quite straightforward, but still not hard to figure out.

1..5 | foreach { "process block" } { "remain block" }
1..5 | foreach { "remain block" }  -Process { "process block" }
1..5 | foreach { "remain block" } -End { "end block" } -Process { "process block" } -Begin { "begin block" }
1..5 | foreach { "remain block 1" } -End { "end block" } -Process { "process block" } { "remain block 2" }
1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } -Begin { "begin block" }
1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } { "remain block 3" }
1..5 | foreach { "process block" } { "remain block 1" } { "remain block 2" } -Begin { "begin block" }
1..5 | foreach { "process block" } { "remain block 1" } { "remain block 2" } { "remain block 3" }

那么这里的模式是什么?

So what's the pattern here?

  • 当传入单个 ScriptBlock 时:简单,它只是转到 -Process(最常见的用法)

  • When there's single ScriptBlock passed in: easy, it just goes to -Process (the most common usage)

当正好传入 2 个 ScriptBlock 时,有 3 种可能的组合

When exactly 2 ScriptBlocks are passed in, there are 3 possible combinations

  1. -过程和-开始 -> 按指定执行
  2. -过程和-End -> 按指定执行
  3. -过程和-RemainingScripts -> Process 变成 Begin,而 RemainingScripts 变成 Process

如果我们运行这两个语句:

If we run these 2 statements:

1..5 | foreach { "process block" } { "remain block" }
1..5 | foreach { "remain block" }  -Process { "process block" }

# Both of them will return:
process block
remain block
remain block
remain block
remain block
remain block

您会发现,这只是以下测试用例的一个特例:

As you will find out, this is just a special case of the following test case:

  • 当传入超过 2 个 ScriptBlock 时,请遵循以下工作流程:

  • When more than 2 ScriptBlocks are passed in, follow this workflow:

  1. 绑定所有指定的脚本块(Begin、Process、End);剩余的 ScriptBlocks 转到 RemainingScripts
  2. 将所有脚本排序为:开始 > 处理 > 剩余 > 结束
  3. 排序的结果是 ScriptBlocks 的集合.我们称这个集合为 OrderedScriptBlocks

  • 如果未绑定开始/结束,则忽略

(内部)基于OrderedScriptBlocks

  • OrderedScriptBlocks[0] 变成了开始
  • OrderedScriptBlocks[1..-2] 变成 Process
  • OrderedScriptBlocks[-1](最后一个)变成 End

举个例子

1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } { "remain block 3" }

订单结果是:

{ "process block" }    # new Begin
{ "remain block 1" }   # new Process
{ "remain block 2" }   # new Process
{ "remain block 3" }   # new End

现在执行结果完全可以预测了:

Now the execution result is completely predictable:

process block
remain block 1
remain block 2
remain block 1
remain block 2
remain block 1
remain block 2
remain block 1
remain block 2
remain block 1
remain block 2
remain block 3

这就是 -RemainingScripts 背后的秘密,现在我们了解了 ForEach-Object 的更多内部行为!

That's the secret behind -RemainingScripts and now we understand more internal behavior of ForEach-Object!

我仍然必须承认没有文档支持我的猜测(不是我的错!),但是这些测试用例应该足以解释我所描述的行为.

Still I have to admit there's no documentation to support my guess (not my fault!), but these test cases should be enough to explain the behavior I described.

这篇关于PowerShell:ForEach-Object 的神秘 -RemainingScripts 参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆