简短问题:有关于ForEach对象的-RemainingScripts参数的详细信息吗?
长问题:
我从上个星期开始学习PowerShell,我正在浏览每个Cmdlet来了解更多细节。基于公共文档,我们知道ForEach对象可以具有开始-进程结束块,如下所示:
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计数/文件计数都是正确的。到目前一切尚好。
现在,有趣的是,下面的命令也起作用,并给出了完全相同的结果:
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。
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因此,如果我将命令更改为:
# 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"! (脚本块在开始时只执行一次)。
这项试验表明:
不过,以上都只是我的胡思乱想。我没有找到文件来支持我的猜测
最后,我们回到我的简短问题:)谁有关于ForEach的-RemainingScripts参数的详细信息?
谢谢。
发布于 2013-05-20 18:31:05
我做了更多的研究,现在有信心回答-RemainingScripts参数在多个ScriptBlocks传入时的行为。
如果您运行以下命令并仔细检查结果,您将发现模式。这并不是很简单,但仍然不难理解。
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" }那么这里的模式是什么?
1. -Process & -Begin -> execute as specified
2. -Process & -End -> execute as specified
3. -Process & -RemainingScripts -> Process becomes Begin, while RemainingScripts becomes Process
如果我们运行这2条语句:
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正如您将发现的,这只是以下测试用例的一个特例:
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_ - If Begin/End are not bound, just ignore
1. (Internally) Re-bind parameters based on _OrderedScriptBlocks_ - OrderedScriptBlocks[0] becomes Begin
- OrderedScriptBlocks[1..-2] become Process
- OrderedScriptBlocks[-1] (the last one) becomes 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现在,执行结果是完全可预测的:
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对象的更多内部行为!
不过,我不得不承认,没有任何文档来支持我的猜测(不是我的错!),但是这些测试用例应该足以解释我描述的行为。
发布于 2013-05-20 08:12:39
这是细节。ValueFromRemainingArguments设置为真,所以您的猜测是正确的。
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.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发布于 2019-10-11 15:26:54
这里有更多的例子支持@方舟的排序假说。
如果指定其他块,那么它的工作方式似乎是有意义的:
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: 即使您通过了空块:
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。
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: 您可以通过更改属性的顺序来更改所发生的事情:
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
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
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: 据我所知,它的目的是支持位置脚本块,您将在其中编写:
1..5 | ForEach-Object { "Begin: $_" } { "Process1: $_" } { "Process2: $_" } { "Process3: $_" } { "End: $_" }大概是这样的:
1..5 | ForEach-Object { "Begin: $_" },{ "Process1: $_" },{ "Process2: $_" },{ "Process3: $_" },{ "End: $_" }这两项产出:
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: https://stackoverflow.com/questions/16644739
复制相似问题