2014年9月26日金曜日

◆Foreach-Objectのパラメータへの疑問

今更の感じはするが、Foreach-Objectの動作。

PS>1..3 | ForEach-Object{"開始"}{"ループ"}{"終了"}
開始
ループ
ループ
ループ
終了

本来、ヘルプを見ると位置オペランドは(-Process)だけなので、なぜこれで動くのだろうとちょっと疑問に思う。

ちなみに、以下は良いが、

PS>1..3 | ForEach-Object{"開始"}{"ループ"}

当然、以下は上と同じ書式なので「ループ」が初期処理とみなされる。

PS>1..3 | ForEach-Object{"ループ"}{"終了"}

なので、

PS>1..3 | ForEach-Object{"ループ"}  -end{"終了"}

とする必要が有りそう。

2 件のコメント:

  1. ForEach-Object{"開始"}{"ループ"}{"終了"}
    としたときは、まず一つ目の値である{"開始"}が、位置パラメータである-Processパラメータにバインドされます。
    残りの{"ループ"}と{"終了"}は-RemainingScriptsパラメータにバインドされます。

    -RemainingScriptsパラメータが何故そういう挙動をするかというと、
    このパラメータはScriptBlock[]を受けられ、ValueFromRemainingArguments = Trueという属性が付いているため、
    バインドされなかった残りのパラメータ値をすべてまとめて配列化してこのパラメータにバインドされるためです。

    結局のところ、実行されているのは以下のコマンドになります。
    ForEach-Object -Process {"開始"} -RemainingScripts {"ループ"},{"終了"}

    つまりはRemainingScriptsにスクリプトブロックがバインドされたときは、-ProcessパラメータでBeginの処理が走ることになって何とも奇妙なのですが、これは構文上の利便性を重視したことによる弊害(?)なんでしょうね。

    ちなみに、-ProcessもScriptBlock[]を受けられるので、
    ForEach-Object {"開始"},{"ループ"},{"終了"}
    という書き方もできます。

    あと個人的にはForEach-Object{"ループ"} -end{"終了"}ではなく、
    ForEach-Object {} {"ループ"} {"終了"}
    とするかな…。

    返信削除
  2. いつもコメントありがとうございます。
    非常にためになりました。

    ヘルプを見ると、RemainingScriptsパラメータは3.0から追記されていますので、BeginやEndが指定されない限り2.0では基本的にProcessが全部引き受けているのですかね・・・。(もしくはヘルプの漏れか・・・)

    >ForEach-Object {} {"ループ"} {"終了"}
    これについては私も実践ではそうすると思います。
    実は今、社内向けの簡単な解説テキストを作っていて、教科書的にはForEach-Object{"ループ"} -end{"終了"}かなぁと思ったりしていました。

    また、よろしくお願いいたします。

    返信削除