Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 767
Adi Inbar Avatar asked Jul 23 '12 22:07

Adi Inbar


People also ask

How does pipe work in PowerShell?

Powershell pipe works in an asynchronous way. Meaning that output of the first cmdlet is available to the second cmdlet immediately one object at the time (even if the first one has not finished executing). and then stop the execution by pressing Control+C you will see part of directory is written to the text file.

What does && do PowerShell?

The && operator executes the right-hand pipeline, if the left-hand pipeline succeeded. Conversely, the || operator executes the right-hand pipeline if the left-hand pipeline failed.

What is InputObject in PowerShell?

InputObject is a generic name used for a parameter that takes pipeline input. It's part of internal PowerShell naming convention and there is nothing special about it.

How do you type a pipeline in PowerShell?

The pipeline character in Windows PowerShell is the vertical bar (also called the pipe: | ). On most U.S. keyboards, it is found on the key with the backslash. You can press Shift + Backslash to get the pipe character.


1 Answers

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>
like image 194
zdan Avatar answered Sep 28 '22 01:09

zdan