首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PowerShell: ForEach-Object的神秘-RemainingScripts参数

PowerShell: ForEach-Object的神秘-RemainingScripts参数
EN

Stack Overflow用户
提问于 2013-05-20 07:37:51
回答 5查看 3.8K关注 0票数 12

简短问题:有关于ForEach对象的-RemainingScripts参数的详细信息吗?

长问题:

我从上个星期开始学习PowerShell,我正在浏览每个Cmdlet来了解更多细节。基于公共文档,我们知道ForEach对象可以具有开始-进程结束块,如下所示:

代码语言:javascript
复制
Get-ChildItem | foreach -Begin { "block1";
    $fileCount = $directoryCount = 0} -Process { "block2";
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}} -End {
    "block3"; "$directoryCount directories and $fileCount files"}

预期结果:1对于"block1“和"block3",对传入的每一项重复"block2”,dir计数/文件计数都是正确的。到目前一切尚好。

现在,有趣的是,下面的命令也起作用,并给出了完全相同的结果:

代码语言:javascript
复制
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块怎么样?根据手册,没有“位置2”的参数。因此,我转向跟踪命令,发现后面的两个脚本块实际上是作为“带有两个元素的RemainingScripts”的IList。

代码语言:javascript
复制
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

因此,如果我将命令更改为:

代码语言:javascript
复制
# 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"}

不过,结果完全一样。

因此,正如您注意到的,所有三个命令都给出了相同的结果。这引发了一个有趣的问题:后面两个命令(隐式)都指定了-Process,但是ForEach-Object意外地使用-Process的参数作为"-Begin"! (脚本块在开始时只执行一次)。

这项试验表明:

  1. -RemainingScripts参数将接受所有未绑定的ScriptBlocks
  2. 当传入3个块时,虽然第一个块进入了-Process,但后来它实际上被用作“开始”,而剩下的两个块变成了“进程”和“结束”。

不过,以上都只是我的胡思乱想。我没有找到文件来支持我的猜测

最后,我们回到我的简短问题:)谁有关于ForEach的-RemainingScripts参数的详细信息?

谢谢。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-05-20 18:31:05

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

如果您运行以下命令并仔细检查结果,您将发现模式。这并不是很简单,但仍然不难理解。

代码语言:javascript
复制
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" }

那么这里的模式是什么?

  • 当有一个ScriptBlock传入时: easy,它只是转到-Process (最常见的用法)
  • 当传入2 ScriptBlocks时,有3种可能的组合
代码语言:javascript
复制
1. -Process & -Begin -> execute as specified
2. -Process & -End -> execute as specified
3. -Process & -RemainingScripts  -> Process becomes Begin, while RemainingScripts becomes Process

如果我们运行这2条语句:

代码语言:javascript
复制
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

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

  • 当传入超过2个ScriptBlocks时,请遵循以下工作流:
代码语言:javascript
复制
1. Bind all scriptblocks as specified (Begin,Process,End); remaining ScriptBlocks go to RemainingScripts
2. Order all scripts as: Begin > Process > Remaining > End
3. Result of ordering is a collection of ScriptBlocks. Let's call this collection _OrderedScriptBlocks_
代码语言:javascript
复制
    - If Begin/End are not bound, just ignore

代码语言:javascript
复制
1. (Internally) Re-bind parameters based on _OrderedScriptBlocks_
代码语言:javascript
复制
    - OrderedScriptBlocks[0] becomes Begin
    - OrderedScriptBlocks[1..-2] become Process
    - OrderedScriptBlocks[-1] (the last one) becomes End

以这个例子为例

代码语言:javascript
复制
1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } { "remain block 3" }

命令的结果是:

代码语言:javascript
复制
{ "process block" }    # new Begin
{ "remain block 1" }   # new Process
{ "remain block 2" }   # new Process
{ "remain block 3" }   # new End

现在,执行结果是完全可预测的:

代码语言:javascript
复制
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对象的更多内部行为!

不过,我不得不承认,没有任何文档来支持我的猜测(不是我的错!),但是这些测试用例应该足以解释我描述的行为。

票数 6
EN

Stack Overflow用户

发布于 2013-05-20 08:12:39

这是细节。ValueFromRemainingArguments设置为真,所以您的猜测是正确的。

代码语言:javascript
复制
help ForEach-Object

-RemainingScripts <ScriptBlock[]>
    Takes all script blocks that are not taken by the Process parameter.

    This parameter is introduced in Windows PowerShell 3.0.
代码语言:javascript
复制
gcm ForEach-Object | select -exp parametersets 

Parameter Name: RemainingScripts
  ParameterType = System.Management.Automation.ScriptBlock[]
  Position = -2147483648
  IsMandatory = False
  IsDynamic = False
  HelpMessage =
  ValueFromPipeline = False
  ValueFromPipelineByPropertyName = False
  ValueFromRemainingArguments = True
  Aliases = {}
  Attributes =
    System.Management.Automation.ParameterAttribute
    System.Management.Automation.AllowEmptyCollectionAttribute
    System.Management.Automation.AllowNullAttribute
票数 1
EN

Stack Overflow用户

发布于 2019-10-11 15:26:54

这里有更多的例子支持@方舟的排序假说。

如果指定其他块,那么它的工作方式似乎是有意义的:

代码语言:javascript
复制
PS C:\> 1..5 | ForEach-Object -Begin { "Begin: $_" } -End { "End: $_" } -Process { "Process: $_" } -RemainingScripts { "R1: $_" },{ "R2: $_" },{ "R3: $_" }
Begin: 
Process: 1
R1: 1
R2: 1
R3: 1
Process: 2
R1: 2
R2: 2
R3: 2
Process: 3
R1: 3
R2: 3
R3: 3
Process: 4
R1: 4
R2: 4
R3: 4
Process: 5
R1: 5
R2: 5
R3: 5
End: 

即使您通过了空块:

代码语言:javascript
复制
PS C:\> 1..5 | ForEach-Object -Begin {} -End {} -Process { "Process: $_" } -RemainingScripts { "R1: $_" },{ "R2: $_" },{ "R3: $_" }
Process: 1
R1: 1
R2: 1
R3: 1
Process: 2
R1: 2
R2: 2
R3: 2
Process: 3
R1: 3
R2: 3
R3: 3
Process: 4
R1: 4
R2: 4
R3: 4
Process: 5
R1: 5
R2: 5
R3: 5

但是,如果不指定-End,则它会执行完全不同的操作。传递给该命令的最后一个scriptblock用于-End

代码语言:javascript
复制
PS C:\> 1..5 | ForEach-Object -Begin { "Begin: $_" } -Process { "Process: $_" } -RemainingScripts { "R1: $_" },{ "R2: $_" },{ "R3: $_" }
Begin: 
Process: 1
R1: 1
R2: 1
Process: 2
R1: 2
R2: 2
Process: 3
R1: 3
R2: 3
Process: 4
R1: 4
R2: 4
Process: 5
R1: 5
R2: 5
R3: 

您可以通过更改属性的顺序来更改所发生的事情:

代码语言:javascript
复制
PS C:\> 1..5 | ForEach-Object  -RemainingScripts { "R1: $_" },{ "R2: $_" },{ "R3: $_" } -Begin { "Begin: $_" } -Process { "Process: $_" }
Begin: 
R1: 1
R2: 1
R3: 1
R1: 2
R2: 2
R3: 2
R1: 3
R2: 3
R3: 3
R1: 4
R2: 4
R3: 4
R1: 5
R2: 5
R3: 5
Process: 

如果您不指定-Begin,它又是不同的。现在,传递的第一个脚本块用于-Begin

代码语言:javascript
复制
PS C:\> 1..5 | ForEach-Object -End { "End: $_" } -Process { "Process: $_" } -RemainingScripts { "R1: $_" },{ "R2: $_" },{ "R3: $_" }
Process: 
R1: 1
R2: 1
R3: 1
R1: 2
R2: 2
R3: 2
R1: 3
R2: 3
R3: 3
R1: 4
R2: 4
R3: 4
R1: 5
R2: 5
R3: 5
End: 

如果您既没有指定-Begin,也没有指定-End,那么它将两者结合在一起。现在,第一个脚本块替换-Begin,最后一个脚本块替换-End

代码语言:javascript
复制
PS C:\> 1..5 | ForEach-Object -Process { "Process: $_" } -RemainingScripts { "R1: $_" },{ "R2: $_" },{ "R3: $_" }
Process: 
R1: 1
R2: 1
R1: 2
R2: 2
R1: 3
R2: 3
R1: 4
R2: 4
R1: 5
R2: 5
R3: 

据我所知,它的目的是支持位置脚本块,您将在其中编写:

代码语言:javascript
复制
1..5 | ForEach-Object { "Begin: $_" } { "Process1: $_" } { "Process2: $_" } { "Process3: $_" } { "End: $_" }

大概是这样的:

代码语言:javascript
复制
1..5 | ForEach-Object { "Begin: $_" },{ "Process1: $_" },{ "Process2: $_" },{ "Process3: $_" },{ "End: $_" }

这两项产出:

代码语言:javascript
复制
Begin: 
Process1: 1
Process2: 1
Process3: 1
Process1: 2
Process2: 2
Process3: 2
Process1: 3
Process2: 3
Process3: 3
Process1: 4
Process2: 4
Process3: 4
Process1: 5
Process2: 5
Process3: 5
End: 
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16644739

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档