2011-02-10 17 views
5

where-objectフィルタスクリプトのスクリプトブロックに親スコープ変数を使用するよりも情報を渡す方が良いでしょうか?PowershellでWhere-Objectでパラメータ化されたスクリプトブロックを使用するにはどうすればよいですか?

背景:

私はソースコントロール対未チェックインされたおよび/または変更されたソースファイルを探し、それをより徹底的な検索を行うことができますパラメータを持つスクリプトを持っています。私はwhere-objectを、スクリプトの入力パラメータに基づいてカスタマイズするスクリプトスコープの変数に含まれるスクリプトブロックオブジェクトを使用して数か所で使用します。

徹底的な検索を要求すると、フィルタは候補ファイルとすべてのTFSファイルを比較して、ファイルがソース管理に含まれていないかどうかを調べます。チェックアウトされたファイルと比較して、ファイルが変更されているがチェックアウトされていないかどうかを確認します。

カスタマイズされたスクリプトブロックは、ソース管理に対してクエリを実行した結果を含むスクリプトスコープの変数を参照します。

私の問題は、グローバルな(スクリプトレベルの)変数を取り除き、必要なすべての情報をスクリプトブロックのパラメータとしてスクリプトブロックに渡すことです。 invoke-commandを使用していた場合、これを行うにはArgumentListパラメータを使用します。 Where-Objectはそれを持っていないようです。スクリプトブロックで親スコープの変数参照を使用することの1つの欠点は、これらの変数を変更できないため、遅延初期化を行うことができないことです(または、少なくとも私は方法をまだ理解していません。 。PowerShellのルールをスコープ)

答えて

6

ちょうどあなたがこのようにそれを行うことができ、キースが述べたものにビットを拡大する:

ps> $x=2 
ps> 1..5 | where (& {param($v); { $_ -eq $v }.getnewclosure() } $x) 
2 

私はのparam宣言を保存するためにimplict $argsの上にクローズしようとしたが、撮影が免除$argsようです。おそらくそれは捕らえられているかもしれませんが、ただ踏みつぶされてしまいます。

$xは、(get-x)のような別の関数呼び出しで簡単に置き換えることができます。

私は本質的に、外側のscriptblocksパラメータで終了するscriptblockを返すscriptblockを呼び出しています。 Keithsと同じ実装ですが、Lambdasの勝利のためにはもう少しサスクイントしました。

私は、クロージャのセマンティクスを取得するためのより控えめな方法がほしいと思っています。それは、私が方法ではなくむしろ入れられてうれしい、と言いました。

$x=2 
1..5 | where (& {[scriptblock]::create('$_ -eq ' + $args) } $x) 


2 

奇妙なことに、ブランドの新しいスクリプトブロックを作成することのようです:あなたは$ argsを周りexsitingスクリプトブロックを閉じることはできませんが、あなたはそれで新しいscripblockを作成することができます

+0

Heheh、ええ、ちょうど私が昼食に私に起こった。また、FilterProcsメソッドが渡されたスクリプトブロックへのフィードを取得する '-ArgumentList'パラメータを取ると考えていました。 –

+0

$ argsがキャプチャを免除されているか、スクリプトブロックの新しいスコープを作成していますか?そのスコープのargsは、親スコープからの$ argsをもはや見ることができません?私は、select-objectを使ってハッシュテーブル式と同様の動作をしました。 $ argsがハッシュテーブル式スクリプトブロックで使用されていた場合、$ script:argsとして明示的にスコープされていなければなりません。ローカルの$ argsはnullです。 – mjolinor

+0

"捕まえられている可能性が高いが、踏みつぶされている可能性が高い。"だからええ。 – x0n

5

あなたは一時スコープを作成し、それがスクリプトブロックの例では、使用中の変数の周りにクロージャを作成してもらうためにスクリプトブロックにGetNewClosure()メソッドを使用することができます。この場合

function FilterProcs([scriptblock]$scriptblock) 
{ 
    Get-Process | Where $scriptblock 
} 

$name = 'Notepad' 

& { 
    $name = 'PowerShell' 
    FilterProcs {$_.Name -eq $name}.GetNewClosure() 
} 

$name 

$nameの内部修正はローカルでのみ表示され、スクリプト スコープでは表示されません。よりよい方法があるかもしれませんが、これはあなたのために働くと思います。

function FilterProcs([scriptblock]$scriptblock, [object[]]$argumentList) 
{ 
    Get-Process | Foreach {if (&$scriptblock @argumentList){$_}} 
} 

FilterProcs {param($name, $id) ($_.Name -match $name -and $_.Id -eq $id)} ` 
      -ArgumentList 'PowerShell_ISE',$pid 

P.S.:

はところで、ここでのForeach-Objectコマンドレットを使用してフィルタリングする別のアプローチですあなたがPowerShellinを維持しているのを見てうれしい。 :-)

+0

礼儀+1 ;-) – x0n

3

-Oisin

既存のgetnewclosureを呼び出すよりも高速です。私は両方のシナリオを10000人の担当者で実行し、$ argsで新しいスクリプトブロックを作成するには5.3秒かかり、パラメータ宣言でgetnewclosureが7.9を要しました。

関連する問題