2008年01月12日

PowerShellにおけるパイプの動作について

久しぶりにPowerShell関連の話題です。2chのスレッド「Windows PowerShell (正式版リリース)1.0」でのパイプの話題を受けて、NyaRuRuさんが「PowerShell で SIGPIPE 連鎖」で以下のようなコードを書いています。

PowerShell 1.0

filter yes {
  while($true){ "y" }
}

filter take([int] $n) { 
  if(0 -gt --$n) { break } 
  $_ 
}

こうやって,

yes | take 10000 | take 1

これがちゃんと 1 行だけ出力して止まる.SIGPIPE 連鎖っぽい.すげえ.

僕も以前似たようなことを試して引っかかったのですが、上記のコードではyesフィルタは1回しか値を返してないと見せかけて、2回返しています。わかりやすいようにyesフィルタを

filter yes {
    while ($true) {
        "say" | Out-Host
        "y"
    }
}

として実行すると、

PS > yes | take 10000 | take 1
say
y
say

と出力されます。フィルタは基本的にprocess節のみの関数なのですが、process節に入るということはすでにパイプから値を取っていることになります。なので、上記のtakeフィルタでは

  1. パイプから値を取る
  2. 終了判定をする
  3. 値を返す

という動作を繰り返すことになり、終了条件を満たしたときにはすでに一個余分に値を取ってしまっているというわけです。

対策として、takeはフィルタではなく関数で以下のように定義します。

function take($n) {
    begin {
        if (0 -ge $n) { break }
    }

    process {
        $_
        if (0 -ge --$n) { break }
    }
}

関数のprocess節内ではパイプからの値はすでに取得してしまっているので終了判定は最後に持っていき、さらにそもそもprocess節に行くべきかどうかを判定するためのbegin節を追加します。実行すると

PS > yes | take 10000 | take 1
say
y

となります。余分なsayがなくなりました。

ここまで書いておいてなんですが、僕も今回書いたことについてはあまり自信がありません。こういった、パイプ関連の動作について詳しく述べている文献は無いものでしょうか。

追記(01/15)
posted by bakemoji at 22:21| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は90日以上新しい記事の投稿がないブログに表示されております。