15

私はこのようなことをしたい:

<statement> | <filter1> | <filter2> if <condition> | <filter3> | <filter4> | <filter5>

<statement>の結果は<filter1>を通過し、次に<condition>が満たされた場合にのみ<filter2>を通過し、次に<filter2>が適用されたかどうかに関係なく残りのフィルターを通過します。これは次と同等です。

if (<condition>) {
  <statement> | <filter1> | <filter2> | <filter3> | <filter4> | <filter5>
} else {
  <statement> | <filter1> | <filter3> | <filter4> | <filter5>
}

これは、特定のスイッチが呼び出された場合にのみ、特定のフィルターが結果セットに適用される関数で役立ちます。条件付きフィルターが長いパイプラインの早い段階で発生する場合、特に複数の条件付きフィルターがある場合は、外側のif-blockを使用してコードを書き込むと、コードが何度も繰り返されます。

これが例です。次の関数は、特定のアカウントが特定のディレクトリサブツリーで持つアクセス許可を示します(たとえばShow-AccountPerms \\SERVERX\Marketing DOMAIN\jdoe、ユーザーDOMAIN\jdoeが\SERVERX\ Marketingの下のディレクトリツリーで持つアクセス許可のレポートを提供します)。

function Show-AccountPerms {
    param (
        [parameter(mandatory = $true)]$rootdir,
        [parameter(mandatory = $true)]$account,
        [switch]$files,
        [switch]$inherited
    )
    gci -r $rootdir `
    |where {$_.psiscontainer} `
    |foreach {
        $dir = $_.fullname
        (get-acl $_.pspath).access `
        | where {$_.isinherited -eq 'False'} `
        |foreach {
            if ($_.identityreference -eq $account) {
                "{0,-25}{1,-35}{2}" -f $_.identityreference, $_.filesystemrights, $dir
            }
        }
    }
}

デフォルトでは、明示的なアクセス許可(フィルターによって適用される| where {$_.isinherited -eq 'False'})とディレクトリ(|where {$_.psiscontainer}フィルターによって適用される)のみが表示されます。

ただし、|where {$_.psiscontainer}-filesスイッチが呼び出された| where {$_.isinherited -eq 'False'}場合は無視し、-inheritedスイッチが呼び出された場合は無視します。これをouterifブロックで達成すると、コードが4倍になり、そのほぼ75%が繰り返しになります。これらのフィルターをインラインに保つ方法はありますが、対応するスイッチのフィルターのみを適用するようにPowerShellに指示するのは誤りですか?

これは単なる例であるため、この関数に固有の回避策には関心がないことに注意してください。私は、この特定のタスクを実行する方法の解決策ではなく、条件付きで配管に関する私の一般的な質問に対する答えを探しています。

4

6 に答える 6

7

フィルタで両方の条件をテストして、どちらかが真の場合にオブジェクトがパイプラインを下ることを許可できます。-or「条件」が演算子の左側にある$true場合は、フィルター条件をテストしたくない場合に結果を出します。

あなたの例を使用するには:

| where {$_.psiscontainer}

になります:

| where {$files -or $_.psiscontainer}

| where {$_.isinherited -eq 'False'}

になります

| where {$inherited -or $_.isinherited -eq 'False'}

一般化するには:

<statement> | <filter1> | <filter2> if <condition> | <filter3> | <filter4> | <filter5>

になります:

<statement> | <filter1> | <-not condition -or filter2> | <filter3> | <filter4> | <filter5>
于 2012-07-23T22:50:10.153 に答える
4

この質問に対するもう1つの答えは、何が求められているかを誤解していると思います。

解決策は次のとおりです。

... | %{if($_ -match "Something"){DoSomethingWith $_ }else{$_}} | ...

これにより、「Something」に一致する要素を除いて、すべての要素が次のフィルターに渡されます。この場合、異なるロジックが実行されます。関数の代わりにパイプライン要素の変更されたバージョンを渡すようにロジックを変更できます。

于 2012-07-25T14:46:29.137 に答える
4

申し訳ありませんが、私はこの質問を放棄するつもりはありませんでした。投稿された回答は私が運転していたものではありませんでしたが、投稿後すぐにそれを行う方法を見つけ、長い間サイトに戻ってきませんでした。解決策が投稿されていないので、これが私が思いついたものです。それは私が質問をしたときに私が考えていたものではなく、あまりきれいではありませんが、どうやらそれがそれを行う唯一の方法です:

<statement> | <filter1> | foreach {if (<condition>) {$_ | <filter2>} else {$_} | <filter3> | <filter4> | <filter5>

したがって、この例では、行

|where {$_.psiscontainer} `

に変更されます

|foreach {if (-not $files) {$_ | where {$_.psiscontainer}} else {$_}} `

|where {$_.isinherited -eq 'False'} `

に変更されます

|foreach {if (-not $inherited) {$_ | where {$_.isinherited -eq 'False'}} else {$_}} `

(はい、通常はと書きますが|foreach {if ($files) {$_} else {$_ | where {$_.psiscontainer}}}|foreach {if ($inherited) {$_} else {$_ | where {$_.isinherited -eq 'False'}}}わかりやすくするためにこのように書きました。)

パイプラインのステージを実行するかスキップするかを決定するために、フィルターの前の状態を1回評価する、よりエレガントなものがあるのではないかと期待していました。このようなもの:

<statement> | <filter1> | if (<condition>) {<filter2>} | <filter3>

(通常の意味ではなく、の特殊なケースif。別のキーワードを使用できます)、または多分

<statement> | <filter1> | (<condition>) ? <filter2> | <filter3>

$_現在のパイプラインの外部で定義されていない限り、条件では無効になります。たとえば、パイプラインがステートメント内に含まれている場合switchは、ステートメントの$_<condition>参照します。switch$_

マイクロソフトに機能を提案したいと思います。これにより、コードがよりエレガントになるだけでなく、より効率的になります。これは、組み込み機能の場合、各反復で同じ独立した条件をテストするのではなく、パイプライン全体に対して1回<condition>評価できるためです。

于 2013-07-24T03:56:33.780 に答える
4

私はあなたが次のようなものを意味していると思います、それは私がちょうど作りました:

function Pipe-If([ScriptBlock]$decider, [ScriptBlock]$pipeElement)
{
    if (&$decider) {
        $pipeElement
    } else {
        {$input}
    }
}

@(1,2,3) | &(Pipe-If {$doDouble} {$input | % { $_ * 2} })

の場合$doDoubleは2、4、6$trueになり、その上$falseでは1、2、3になります。

ここで重要なのは、のような任意のパイプ要素を% { $_ * 2}ScriptBlockとしてカプセル化できることと、先頭に{$input | % { $_ * 2 } }を付けることでパイプ要素に戻すことができること&です。

インスピレーションを得るためにhttps://devblogs.microsoft.com/powershell/diy-ternary-operatorを使用しました。


重要な注意点。 次のようなものは使用しないでください。

filter Incorrect-Pipe-If([ScriptBlock]$decider, [ScriptBlock]$pipeElement) {
    if (&$decider) {
        $_ | &$pipeElement
    } else {
        $_
    }
}

@(1,2,3) | Incorrect-Pipe-If {$doDouble} {$_ | % { $_ * 2} }

これにより%、パイプライン内のオブジェクトごとに1回ずつ、複数回実行されます。 Pipe-Ifコマンドを1回だけ正しく実行し、%オブジェクトのストリーム全体を送信します。

上記の例では、それは問題ではありません。しかし、コマンドがその場合tee bla.txt、違い重要です。

于 2013-11-21T14:41:17.543 に答える
1

これはMarnixKloosterの答えに似ていますが、操作が簡単です。この定式化の利点は、実行するコードの構文にあります。通常のパイプラインブロックにはるかに近いです。基本的には、必要なものをすべて{}(中かっこ)で囲む必要があります。

Marnixのスクリプトと同様に、これはブロッキング関数であることに注意してください。パイプラインの結果は$Inputに収集され、関数自体は1回だけ実行されます。$ pipeElementコードのみが複数回実行され、-Executeがtrueの場合にのみ実行されます。

function Conditional_Block([bool]$Execute,[ScriptBlock]$PipeElement)
{
    if ($Execute) 
        {$ExecutionContext.InvokeCommand.NewScriptBlock("`$(`$Input|$PipeElement)").invoke()}
    else
        {$input}
}

この関数を使用するために、すでに定義されている必要はありません。実際にそれを使用したい関数内で定義し、関数が完了するとそれを非表示にすることができます。

-executeパラメーターは、ステップの実行を有効/無効にします。ブール結果のある式はすべて機能します。たとえば、$(1 -eq 0)は$falseと同じように機能します。

@(1,2,3)|Conditional_Block $true  {?{$_ -le 2}}|%{"'$_'"}
@(1,2,3)|Conditional_Block $false {?{$_ -le 2}}|%{"'$_'"}
于 2018-06-05T22:23:48.107 に答える
0

もう1つのオプションは、(タイプのSystem.Management.Automation.ActionPreference)グローバル設定フラグを使用して、パイプラインフィルターが何かを実行するかどうかをユーザーが判断できるようにすることです。

たとえば、$progressPreferenceは次の値に設定できます。

  • SilentlyContinue
  • 止まる
  • 継続する
  • お問い合わせ
  • 無視

この設定フラグはWrite-Progress、目的の動作を決定するためにによって使用されます。

たとえば、アイテムをカウントしてプログレスバーを表示するパイプラインフィルターがある場合、がに設定されているShow-Progress場合にのみこのプログレスバーが表示されます。$progressPreferenceContinue

独自のパイプラインフィルターで類似の構造を使用できます。

于 2014-11-15T01:26:02.660 に答える