2013-05-20 19 views
5

短い質問:誰でもForEach-Objectの-RemainingScriptsパラメータに関する詳細情報を持っていますか?PowerShell:ForEach-Objectの不思議な-RemainingScriptsパラメータ

ロング質問:

私はちょうど先週からPowerShellを学び始めたと私はより多くの詳細を学ぶために、各コマンドレットを通じてつもりです。

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

結果が期待されています:「ブロック1」と「BLOCK3」、「ブロック2」のための1時間がために繰り返されるパブリックのドキュメントに基づいて、我々は、これはのようなのForEach-Objectは、開始・プロセス・エンド・ブロックを持つことができます知っています渡された各項目、およびdirカウント/ファイル数はすべて正しいです。ここまでは順調ですね。 foreachのために渡された

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

わずか3 ScriptBlocks:

は今、どのような興味深いのは、次のコマンドも作品とまったく同じ結果を与える、です。マニュアルに基づいて、最初のものは「プロセス」(位置1)に移動します。しかし残りの2はどうですか?マニュアルによれば、「位置2」のパラメータはありません。そこで私はTrace-Commandに目を向けると、後の2つのスクリプトブロックが実際にRemainingScriptsに「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 

は、だから私は、これにコマンドを変更した場合:

# 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つのコマンドはすべて同じ結果を示します。これは興味深い質問を引き起こします:後で2つのコマンド(暗黙的に)を指定した-ProcessのどちらもForEach-Objectは意外にも、-Processの引数を "-Begin"として使用することになります!(スクリプトブロックは最初に1回実行されます)。

この実験は示唆:

  1. -RemainingScriptsパラメータは、最初のものは-Processに行くが、3つのブロックは、渡された場合、後でそれは実際に「開始」として使用されている全ての未結合ScriptBlocks
  2. がかかります残りの2つは "プロセス"と "終了"になります

まだ、上記はすべて私の野生の推測です。私の推測をサポートするためのドキュメントが見つかりませんでした

最後に、私は短い質問に戻ります:) ForEach-Objectの-RemainingScriptsパラメータに関する詳細な情報はありますか?

ありがとうございました。

答えて

3

私はより多くの研究を行なったし、今で複数ScriptBlocksが渡されたときに-RemainingScriptsパラメータの動作に答える自信。

次のコマンドを実行し、結果を検査した場合慎重に、パターンを見つけるでしょう。これは簡単ではありませんが、把握するのはまだ難しいことではありません。

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がでそこに渡されたとき:ちょうど2 ScriptBlocksが渡されたときに簡単、それだけで(最も一般的な使用法)-Processに行く

  • 、3つの可能な組み合わせ

      があります
    1. -Process & -Begin - >
    2. -Process & -End
    3. を指定として実行 - > を指定として実行
    4. -Process & -RemainingScripts - >プロセスが開始となり、RemainingScriptsはプロセス

なるが、我々はこれらの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 

を、これは単なる特別です次のテストケースの場合:

  • 2つ以上のScriptBlocksが渡される場合は、このワークフロー:

    1. 指定されたすべてのスクリプトブロックをバインドします(開始、プロセス、終了)。残りのScriptBlocksはRemainingScriptsに移動
    2. すべてのスクリプトを次のように並べ替えます。Begin> Process> Remaining> End
    3. 注文の結果はScriptBlockのコレクションです。 [ちょうどOrderedScriptBlocks

      • OrderedScriptBlocksに基づいて
    4. (内部的に)再バインドパラメータを無視し、開始/終了がバインドされていない場合のはOrderedScriptBlocks

      • このコレクションを呼びましょう0]がBeginになる
      • OrderedScriptBlocks [1 ..

        :-2]となってプロセス
      • OrderedScriptBlocks [-1](最後のものは)終了

だが

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 

実行結果は次のようになります。完全に予測:

-RemainingScriptsの秘密だと、今、我々はをForEach-Objectの複数の内部動作を理解
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 

私の推測をサポートするためのドキュメントはありませんが、これらのテストケースは、私が記述した動作を説明するのに十分であるはずです。

1

ここにその詳細があります。 ValueFromRemainingArgumentsはtrueに設定されているため、推測は正しいです。

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 
+0

ありがとうございますAndy、あなたはポイント1に答えました: "-RemainingScriptsパラメーターはすべての非結合ScriptBlocksを取りますが、ポイント#2を説明しませんでした"最初の1つはプロセスに行きますが、3ブロックが渡されます。これは実際にはこの質問の主な理由です。私はより多くの研究をしましたが、今は#ポイント2に答えることができると思います。しかし、あなたの答えとパラメータを検査する技術に感謝します。申し訳ありませんが、私はあなたの答えを投票する権限がありませんが、非常に便利です。 –