なり、なんとendブロックが実行されていません。-Firstを使っても、同様にendは実行されません。StopUpstreamCommandsExceptionというのはパイプライン処理を打ち切って、そこまでの出力値を正しくパイプライン出力として出してくれますが、やってくれるのはそこまでで、最後のendブロック処理はしてくれません。これは十分注意が必要な点で、自作関数内でbeginブロックで確保したリソースをprocessブロックで利用して、endブロックで解放する...という、いかにも書いてしまいそうなパターンは、実はNGなんですね。-Firstを使うだけでアウトです。リソースの取り回しはどうするのが良いの、って話もありますが、それはまたの機会にしましょう。(1/5追記)あえとすさんの記事が参考になります。この方法ではパイプライン下流でthrowされた場合はトラップできないぽいですね。コマンドレットクラスの場合はIDisposable実装で良さそうです。ここからは私見ですが、StopUpstreamCommandsExceptionが後付けかつ非パブリックなところとか、パイプラインを合法的に脱出するステートメントが今に至るまで用意されていないところとか、パイプラインを何とかして途中で打ち切っても、endは実行されないところとかを見ていると、そもそもPowerShellではパイプライン処理の中断というのは、あまり想定してない操作なのかなぁ、という気がしてきています。上記のような裏技を使って回避するのも一案ではあるとは思いますが、そもそも「パイプライン処理の中断はイレギュラー」と考えて、そういう処理は避けて、必要に応じて別のアプローチを取ることも考えた方がいいのかもしれません。2012が始まりました。初日は私が担当させていただきます。3.0の新機能!...ではなく、初心に返って、PowerShellの「関数」ってどう書くのがいいのかというお話をします。1.0から変わっていない基本の話です。これは今までずっと書きたかったネタですがなかなか書く暇がなくて放置してたものです。3.0の話はきっと他の皆さんが書いて下さるはず!私もまた順番が回ってきたら書こうと思います。PowerShellの関数は従来言語とだいぶ違うPowerShellを使いこなすようになってくると、他の言語を使う時と同じで、定型処理は関数として一つにまとめたくなってきます。ところが他の言語と同じような感覚で関数を書くと、どうもうまくいかないのです。引数にフォルダパスとフォルダ名を指定すると、指定フォルダが存在すればFalseを返し、存在しなければ作成してTrueを返す関数を書いてみました。NewFolderと呼び出せばいいんですが(まあ最初はここもつまづきポイントではありますが)、この実行結果は以下のようになります。NewFolderTrueフォルダが作成されてTrueが返却されることを想定していたのに、なんか余計な出力が混じってしまっています。なんでしょうこれは?実はPowerShell関数内で値が出力されると、returnキーワードがついてなくてもすべて呼び出し元に出力されるという仕様なのです。PowerShellにおけるreturnキーワードの効果は「後続処理を打ち切って呼び出し元に戻る。returnの後に値が指定してあればそれを最後の値として戻す」となります。そのため、呼び出し元に返したくない出力が関数内にある場合は、すべて[void]にキャストしたり|Out-Nullとしてリダイレクトするなどして出力を破棄する必要があるのです。ように破棄してやる必要があるわけです。先ほどの例を見ると、「いやいやなんでそんな訳のわからない仕様なんだよ、returnあるときだけ値返せよ」とお思いかと思います。これはPowerShellの特長の一つである、コマンドのパイプラインによる連携を行うための仕様なんです。ここでコマンドを繋ぐパイプラインがどういう動作をしてるか、おさらいします。{$_.Path}これはハンドル数が500以上のプロセスのメインモジュールファイルのパスを取得するというコマンドで、別に何の変哲もありません。稼働中のすべてのプロセスの一覧を配列として取得する。�@で取得した配列を走査して、Handlesプロパティの値を調べる。Handlesが500以上のオブジェクトだけ抽出した配列を生成する。�Aで生成した配列を列挙して、{}内のスクリプトをそれぞれ実行する。稼働中の1つのプロセスオブジェクトを取得して次のコマンドへ送る。そのプロセスのハンドル数が500以上なら、次のコマンドへ送る。そのプロセスに対して{}内のスクリプトを実行する。未取得のプロセスが残っていれば�@に戻る。いう動きをしています。パイプラインの手前で一旦すべての処理を終えてから、出力オブジェクトがまとめて配列という形で次のコマンドに送られるのではなく、オブジェクトがパイプラインの先頭から末尾に向けて1つずつ通過していき、それが先頭コマンドの出力オブジェクト数だけ繰り返される、という動作をしているのです。これがPowerShellのパイプライン処理が、従来の処理系での関数と決定的に違うところで、パイプラインによって複数のコマンドが、あたかももとからあった単一のコマンドのように密に連携するわけです。(この処理、.NETのLINQにちょっと似てると思う方もいらっしゃると思います。LINQとは全然違うものです。類似点も多いのでいずれ比較なんかを書きたいと思ってます)パイプラインで連携可能な関数の書き方さて、先ほどのパイプラインの話ではコマンドレットを連携させていました。PowerShellにおいてはコマンドレットも関数も、それが.NETのクラスかPowerShellのスクリプトなのかの違いがあるだけで、基本は同じ「コマンド」です。関数もコマンドレットと同様、適切な記述をおこなえば、パイプラインでコマンド同士を連携させることが可能です。以下に、Get-Repeatという関数の例を挙げます。この関数は-Textパラメータに文字列を指定し、-Countパラメータに回数を指定すると、指定文字列を指定回数分連結した文字列を出力する、という何の変哲もない関数です。パイプラインからの入力を受け付け、次のパイプラインへ出力することを想定した作りになっています。}}以下は実行例です。2ababcdcdこのように、パラメータに値を指定してもパイプラインから入力しても、スカラー値(配列ではない単一のオブジェクト)でも配列でも、正しく処理されています。この関数をポイントごとに見ていきましょう。PowerShellの正式な関数はparam節、beginブロック、processブロック、endブロックに分かれます。param節にはパラメータを指定します。beginブロックにはパイプラインで連携した際、最初の1回だけ実行される初期化処理、endブロックには最後の1回だけ実行される後始末処理を記述します。beginとendは今回の例では内容を省略しています。processブロックには、パイプラインから入力された1つのオブジェクトに対してその都度実行される処理を記述します。ちなみに、コマンド�@|コマンド�A|コマンド�Bとある場合、各コマンドにおけるbegin,process,endブロックは次のような順番で呼び出されます。コマンド�@begin→コマンド�Abegin→コマンド�Bbegin→{コマンド�@process→コマンド�Aprocess→コマンド�Bprocess→コマンド�@process...}→コマンド�@end→コマンド�Aend→コマンド�Bendprocessブロックでの処理は、通常はパイプラインだけではなくパラメータからも値を入力できるようにしておきます。今回のパラメータには「このパラメータは必須である」を意味するMandatory=$trueもあわせて指定しています。ようにパラメータの型を配列型にしておくことで、どちらを指定しても処理できるようにしています。processブロックではパラメータ経由で配列値が渡された場合に、各要素に対して処理を行うためforeachループを設けています。ちなみにスカラー値が渡された場合もforeachは問題なく処理します。processブロック内では、returnは記述しません。returnするとその時点で関数が終了してしまうので正しくすべての出力ができなくなってしまいます。この例の関数のように入力型と出力型が同一の場合は、processブロックでは1オブジェクトの入力に対して、1オブジェクトを出力するようにしておくと、他のコマンドと連携させやすくなります。Where-Objectコマンドレットのようにフィルタ処理を行う関数の場合は、条件によっては何も出力しないようにします(空の配列とか$nullを返すのではないことに注意)。入力オブジェクトから何らかの配列値を出力する場合もありえます。最低限、これらのポイントを押さえて関数を記述すると、他のコマンドとパイプラインで連携しやすい、PowerShellらしい関数を書くことができると思います。まとめPowerShellでは従来言語と同じ感覚で関数を書くと、うまくいかないことが多いです。処理をひとまとめにしたいというニーズだけならばそれでも問題ないのですが、関数同士を組み合わせたいときに問題が顕在化します。パイプラインの真の動作を理解し、パイプラインの中に組み込んで動作させることを想定した関数を記述すると、他のコマンドレットあるいは自作関数と連携しやすくなり、PowerShellの真の力を解放することができると思います。2012の1日目にしてはえらい固いネタかもですが、基本をおさらいするのも大事ですよね。明日は@jsakamotoさんの番です。@mutaguchiBooks書籍版。