Is it possible to pipe conditionally in Powershell, i.e. execute an element of a pipeline only if a condition is met?

powershell if ($variable)
powershell pipeline variable
valuefrompipeline
powershell where-object
powershell filter
powershell select-object to string
powershell select where
powershell data

I want to do something like this:

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

The results of <statement> run through <filter1>, then they run through <filter2> only if <condition> is met, then through the remaining filters regardless of whether <filter2> was applied. This is the equivalent of:

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

This would be useful in functions where a given filter is applied to the result set only if a certain switch was invoked. If the conditional filter occurs early in a long pipeline, writing it with an outer if-block results in a lot of repetition of code, especially if there is more than one conditional filter.

Here's an example. The following function shows the permissions a given account has in a given directory subtree (e.g. Show-AccountPerms \\SERVERX\Marketing DOMAIN\jdoe gives a report of permissions that the user DOMAIN\jdoe has in the directory tree under \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
            }
        }
    }
}

By default, it only shows explicit permissions (enforced by the | where {$_.isinherited -eq 'False'} filter), and only on directories (enforced by the |where {$_.psiscontainer} filter).

However, I want to ignore |where {$_.psiscontainer} if the -files switch is invoked, and ignore | where {$_.isinherited -eq 'False'} if the -inherited switch is invoked. Accomplishing this with outer if blocks would quadruple the code, and almost 75% of it would be repetition. Is there a way to keep these filters in-line but instruct powershell to only apply them of the corresponding switch is false?

Please note that this is just an example, so I'm not interested in any workarounds specific to this function. I'm looking for an answer to my general question regarding piping conditionally, not a solution for how to accomplish this particular task.

You can test for both conditions in your filter allowing the object down the pipeline if either one is true. If your "condition" is on the left side of the -or operator, make it result to $true if you don't want your filter condition tested.

To use your example:

| where {$_.psiscontainer}

becomes:

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

and

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

becomes

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

To generalise:

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

becomes:

<statement> | <filter1> | <-not condition -or filter2> | <filter3> | <filter4> | <filter5>

Chapter 7. Conditions, Verifying Whether an Array Contains a Particular Element · Where-Object When you hit (enter)), PowerShell executes the comparison. The result is always True (condition is met) or False (condition not met). 4 -eq 10 The following conditional statement would evaluate to true only if both comparisons evaluate to true:. Is it possible to pipe conditionally in Powershell, i.e. execute an element of a pipeline only if a condition is met? block simply echo all the objects if a

I think the other answer to this question misunderstands what is being asked.

The solution lies in the following:

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

What this will do, is pass all elements through to the next filter, EXCEPT those that match "Something", in which case it does different logic. The logic can be changed to make it pass an altered version of the pipeline element instead of a function.

PowerShell Basics: Working with the If Statement -- Microsoft , Every programming and scripting language has had some sort of way to perform a conditional operation using an If statement as part of flow  The pipeline is one of those features that make PowerShell such a natural language. However, all functions are not created equal in PowerShell, and not all kinds of functions will work with the pipeline. Functions that need to be aware of the pipeline must be built with pipeline support.

I think you mean something like the following, which I just concocted:

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

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

results in 2, 4, 6 if $doDouble is $true, and on $false it results in 1, 2, 3.

The key here is that an arbitrary pipe element like % { $_ * 2} can be encapsulated as a ScriptBlock as {$input | % { $_ * 2 } }, and that it can be converted back to a pipe element by prepending &.

I used http://blogs.msdn.com/b/powershell/archive/2006/12/29/dyi-ternary-operator.aspx for inspiration.


Important note. Don't use something like this:

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

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

This causes % to be executed multiple times, once for each object in the pipeline. Pipe-If correctly executes the % command just once, and sends it the entire stream of objects.

In the example above that is not a problem. But if the command is tee bla.txt then the difference is important.

Deep Dive: PowerShell Loops and Iterations, How PowerShell ForEach Iteration Works; Foreach with Range If the condition is $true the body of the loop is executed, and if it is returns probably want to suppress by piping to Out-Null , assigning to can set the enumerator it initial position, i.e, before the first element in 393,267 People Reached. Most PowerShell cmdlets are designed to support pipelines. In most cases, you can pipe the results of a Get cmdlet to another cmdlet of the same noun. For example, you can pipe the output of the Get-Service cmdlet to the Start-Service or Stop-Service cmdlets. This example pipeline starts the WMI service on the computer:

Sorry, I didn't mean to abandon this question. The answers that were posted weren't what I was driving at, but I figured out a way to do it shortly after posting, and didn't come back to the site for a long time. Since a solution hasn't been posted, here's what I came up with. It's not quite what I had in mind when I asked the question and it isn't too pretty, but apparently it's the only way to do it:

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

So, in the example, the line

|where {$_.psiscontainer} `

would be changed to

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

and

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

would be changed to

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

(Yes, normally I'd write that as |foreach {if ($files) {$_} else {$_ | where {$_.psiscontainer}}}, and |foreach {if ($inherited) {$_} else {$_ | where {$_.isinherited -eq 'False'}}} but I did it this way for clarity.)

I was hoping there might be something more elegant, that would evaluate a condition in front of the filter once to determine whether to execute or skip a stage of the pipeline. Something like this:

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

(a special case of if, not the usual meaning; a different keyword could be used), or maybe

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

$_ would be invalid in the condition, unless it's defined outside the current pipeline, for example if the pipeline is contained within a switch statement, $_ in the <condition> would refer the switch statement's $_.

I think I'll make a feature suggestion to Microsoft. This would not only make the code more elegant, it would be more efficient as well, because if it's a built-in feature, <condition> could be evaluated once for the entire pipeline, rather then testing the same independent condition in each iteration.

about_Pipelines, Combining commands into pipelines in the PowerShell For another example, you can pipe the output of Get-Item or Get-ChildItem within the PowerShell registry bound to cmdlet parameters and processed during pipeline execution. ByPropertyName: The parameter accepts input only when the input  About Operators. 08/28/2018; 11 minutes to read; In this article Short description. Describes the operators that are supported by PowerShell. Long description. An operator is a language element that you can use in a command or expression. PowerShell supports several types of operators to help you manipulate values. Arithmetic Operators

If - Conditionally perform command - Windows CMD, NOT perform the command if the condition is false. IF will only parse numbers when one of (EQU, NEQ, LSS, LEQ, GTR, GEQ) is used. It is possible (though not a good idea) to create a string variable called %ERRORLEVEL% (user variable) if When piping commands, the expression is evaluated from left to right, so. This document covers the schema of an Azure Pipelines YAML file. To learn the basics of YAML, see Learn YAML in Y Minutes . Azure Pipelines doesn't support all YAML features. Unsupported features include anchors, complex keys, and sets. Also, unlike standard YAML, Azure Pipelines depends on seeing stage, job, task, or a task shortcut like

[PDF] PowerShell, A simple example is using Select-Object to only show the Name property of a file More advanced usage of the pipeline allows us to pipe the output of a cmdlet Powershell supports standard conditional logic operators, much like many programming Here the else commands are executed if the if conditions are not met:. It appears to me that what's happening is that when the first cmdlet in a pipeline returns no output, Powershell interprets this as equivalent to piping in an empty array. The explicit parameters are bound first (named or positional), but in this case the empty array takes over as piped in input to -Path.

[PDF] PowerShell Notes for Professionals, Section 1.3: The Pipeline - Using Output from a PowerShell cmdlet. 3 Section 6.3: If conditional shorthand Section 21.1: Piping and Splatting Increases the value of a variable, assignable property, or array element by 1 if the commands inside the brackets ({}) are only executed if the conditions inside the if(()) are met. I'm converting a CSV to SQL inserts and there's a null-able text column which I need to quote in case it is not NULL. I would write something like the following for the conversion: Import-Csv da

Groovy Language Documentation, Spreading method arguments; Spread list elements; Spread map elements Escaping is only allowed for the slash character, i.e. /\/folder/ will be a slashy InvokerHelper class Main extends Script { (1) def run() { (2) println 'Groovy world!' 2, if the precondition is not met, pass the message to the next handler in the chain 

Comments
  • That works fine if the filter is a where clause, but not if the filter itself doesn't evaluate a condition, e.g. if you want to pass the objects through Select-String <pattern> only if the condition is true. | -not <condition> -or <filter2> | doesn't work, e.g. | -not $switch -or select-string 'sometext' |. Keep in mind what "filter" means - a command that takes pipeline input and sends the results down the pipeline. The condition test would be a part of <filter2>, and therefore only works if the filter command evaluates a condition (i.e., is a where).
  • The other answerer understood correctly. The idea isn't to apply a condition to each object and process it differently based on whether it meets the condition; it's to test a condition that's independent of the objects in the pipeline and apply the filter to all objects but only if the condition is true - equivalent to using if to execute the pipeline with or without one stage, but without repeating a lot of code. In my example, $files isn't something that's compared with each object, it's a simple switch that determines whether one filter will be applied to all objects or none.
  • However, this answer was helpful in that it introduced the idea of having the else block simply echo all the objects if a condition isn't met. The idea is if (<condition that has nothing to do with $_>) {<pipe $_ through this filter>}, otherwise skip this filter and move on to the next stage of the pipeline. The else {$_} is effectively a workaround for that last part, skipping the stage of the pipeline and moving on to the next.
  • This looks to me like essentially the same solution (workaround, really, since it's not quite what I wanted) as in my answer, except it's wrapped in a function. I see that as a step in the opposite direction, because not only does it rely on the availability of an external function without significantly compacting the code, it actually makes it less efficient, because the condition is still evaluated for each iteration, but now it makes an external functional call to execute the same logic.
  • @AdiInbar You were asking, "Is there a way to keep these filters in-line but instruct powershell to only apply them of the corresponding switch is false?" And you said that you're looking for "something more elegant, that would evaluate a condition in front of the filter once to determine whether to execute or skip a stage of the pipeline." My &(Pipe-If {<condition>} {$_| <filter> }) is exactly such a syntax. Whether or not to use a function (with likely no performance impact) depends on context, and whether one prefers foreach {if (<condition>) {$_ | <filter>} else {$_} is preference.
  • I'm pretty sure the OP either misunderstood your answer or doesn't understand how, for example, for-each actually works. It's nothing more than a function, like yours, and anything MS might provide would necessarily work in one of the 2 ways presented in this thread. That is, the function must either be blocking (like this one) or require that the condition be re-evaluated for each element in the pipeline. I'm rather certain that no other way is possible, even in theory, without dynamic code.
  • did you make the feature request? If you post a link other people may support this request.
  • I'm not quite sure what you're trying to say, but it sounds like you misread the question, because this doesn't appear to be at all related. This isn't about interactive user decisions, progress bars, or error handling, it's about executing a section of the pipeline if a given independent condition is true and skipping that section if the condition is false. By "independent" I mean not based on the content of the pipelined objects, i.e. if the condition is true, that section is executed for all objects; if false, it's not executed for any object.
  • @AdiInbar The original question needed a solution to skip pipelines from executing, this describes a scenario to do exactly that, and without resorting to complex code constructions. What is your confusion?
  • I was hoping there was a way to do it "inline", without the need to keep reevaluating, at each iteration, a condition that's immutable for the duration of the pipeline. That doesn't appear to be possible. I might submit it as a feature request.
  • If you think this addresses the question, please give an example.
  • This solution works without reevaluating, but relies upon a switch within the filter that responds to a preference flag. Not all filter may provide such an option, but it can work for your own filters.