PowerShell Inline If (IIf)

2019-01-30 18:56发布

问题:

How do I create an a statement with an Inline If (IIf, see also: http://en.wikipedia.org/wiki/IIf or ternary If) in PowerShell?

If you also think that this should be a native PowerShell function, please vote this up: https://connect.microsoft.com/PowerShell/feedback/details/1497806/iif-statement-if-shorthand

回答1:

You can use the PowerShell’s native way:

"The condition is " + (&{If($Condition) {"True"} Else {"False"}}) + "."

But as this adds a lot of parenthesis and brackets to your syntax, you might consider the follow (probably one of the smallest existing) CmdLet:

Function IIf($If, $Right, $Wrong) {If ($If) {$Right} Else {$Wrong}}

Which will simplify your command to:

"The condition is " + (IIf $Condition "True" "False") + "."

Added 2014-09-19:

I have been using the IIf cmdlet now for a while and I still think it will make syntaxes more readable in a lot of cases but as I agree with Jason’s note about the unwanted side effect that both possible values will be evaluated even obviously only one value is used, I have changed the IIf cmdlet a bit:

Function IIf($If, $IfTrue, $IfFalse) {
    If ($If) {If ($IfTrue -is "ScriptBlock") {&$IfTrue} Else {$IfTrue}}
    Else {If ($IfFalse -is "ScriptBlock") {&$IfFalse} Else {$IfFalse}}
}

Now you might add a ScriptBlock (surrounded by {}'s) instead of an object which will not be evaluated if it is not required as shown in this example:

IIf $a {1/$a} NaN

Or placed inline:

"The multiplicative inverse of $a is $(IIf $a {1/$a} NaN)."

In case $a has a value other than zero, the multiplicative inverse is returned; otherwise, it will return NaN (where the {1/$a} is not evaluated).

Another nice example where it will make a quiet ambiguous syntax a lot simpler (especially in case you want to place it inline) is where you want to run a method on an object which could potentially be $Null. The native ‘If’ way to do this, would be something like this:

If ($Object) {$a = $Object.Method()} Else {$a = $null}

(Note that the Else part is often required in e.g. loops where you will need to reset $a.)

With the IIf cmdlet it will look like this:

$a = IIf $Object {$Object.Method()}

(Note that if the $Object is $Null, $a will automatically be set to $Null if no $IfFalse value is supplied.)


Added 2014-09-19:

Minor change to the IIf cmdlet which now sets the current object ($_ or $PSItem):

Function IIf($If, $Then, $Else) {
    If ($If -IsNot "Boolean") {$_ = $If}
    If ($If) {If ($Then -is "ScriptBlock") {&$Then} Else {$Then}}
    Else {If ($Else -is "ScriptBlock") {&$Else} Else {$Else}}
}

This means you can simplify a statement (the PowerShell way) with a method on an object that could potentially be $Null.

The general syntax for this will now be $a = IIf $Object {$_.Method()}. A more common example will look something like:

$VolatileEnvironment = Get-Item -ErrorAction SilentlyContinue "HKCU:\Volatile Environment"
$UserName = IIf $VolatileEnvironment {$_.GetValue("UserName")}

Note that the command $VolatileEnvironment.GetValue("UserName") will normally result in an "You cannot call a method on a null-valued expression." error if the concerned registry (HKCU:\Volatile Environment) doesn’t exist; where the command IIf $VolatileEnvironment {$_.GetValue("UserName")} will just return $Null.

If the $If parameter is a condition (something like $Number -lt 5) or forced to a condition (with the [Bool] type), the IIf cmdlet won't overrule the current object, e.g.:

$RegistryKeys | ForEach {
    $UserName = IIf ($Number -lt 5) {$_.GetValue("UserName")}
}

Or:

$RegistryKeys | ForEach {
    $UserName = IIf [Bool]$VolatileEnvironment {$_.OtherMethod()}
}


回答2:

'The condition is {0}.' -f ('false','true')[$condition]


回答3:

Actually Powershell gives back values that haven't been assigned

$a = if ($condition) { $true } else { $false }

Example:

"The item is $(if ($price -gt 100) { 'expensive' } else { 'cheap' })"

Let's try it out:

$price = 150
The item is expensive
$price = 75
The item is cheap


回答4:

Here is another way:

$condition = $false

"The condition is $(@{$true = "true"; $false = "false"}[$condition])"


回答5:

Function Compare-InlineIf  
{  
[CmdletBinding()]  
    Param(  
        [Parameter(  
            position=0,  
            Mandatory=$false,  
            ValueFromPipeline=$false  
        )]  
        $Condition,  
        [Parameter(  
            position=1,  
            Mandatory=$false,  
            ValueFromPipeline=$false  
        )]  
        $IfTrue,  
        [Parameter(  
            position=2,  
            Mandatory=$false,  
            ValueFromPipeline=$false  
        )]  
        $IfFalse  
    )  
    Begin{  
        Function Usage  
        {  
            write-host @"  
Syntax  
    Compare-InlineIF [[-Condition] <test>] [[-IfTrue] <String> or <ScriptBlock>]  
 [[-IfFalse] <String> or <ScriptBlock>]  
Inputs  
    None  
    You cannot pipe objects to this cmdlet.  

Outputs  
    Depending on the evaluation of the condition statement, will be either the IfTrue or IfFalse suplied parameter values  
Examples  
   .Example 1: perform Compare-InlineIf :  
    PS C:\>Compare-InlineIf -Condition (6 -gt 5) -IfTrue "yes" -IfFalse "no"  

    yes

   .Example 2: perform IIF :  
    PS C:\>IIF (6 -gt 5) "yes" "no"  

    yes  

   .Example 3: perform IIF :  
    PS C:\>IIF `$object "`$true","`$false"  

    False  

   .Example 4: perform IIF :  
    `$object = Get-Item -ErrorAction SilentlyContinue "HKCU:\AppEvents\EventLabels\.Default\"  
    IIf `$object {`$_.GetValue("DispFilename")}  

    @mmres.dll,-5824  
"@  
        }  
    }  
    Process{  
        IF($IfTrue.count -eq 2 -and -not($IfFalse)){  
            $IfFalse = $IfTrue[1]  
            $IfTrue = $IfTrue[0]  
        }elseif($iftrue.count -ge 3 -and -not($IfFalse)){  
            Usage  
            break  
        }  
        If ($Condition -IsNot "Boolean")  
        {  
            $_ = $Condition  
        } else {}  
        If ($Condition)  
        {  
            If ($IfTrue -is "ScriptBlock")  
            {  
                &$IfTrue  
            }  
            Else  
            {  
                $IfTrue  
            }  
        }  
        Else  
        {  
            If ($IfFalse -is "ScriptBlock")  
            {  
                &$IfFalse  
            }  
            Else  
            {  
                $IfFalse  
            }  
        }  
    }  
    End{}  
}  
Set-Alias -Name IIF -Value Compare-InlineIf  


回答6:

Frome here: https://blogs.msdn.microsoft.com/powershell/2006/12/29/diy-ternary-operator/

Relevant code:
# —————————————————————————
# Name:   Invoke-Ternary
# Alias:  ?:
# Author: Karl Prosser
# Desc:   Similar to the C# ? : operator e.g. 
#            _name = (value != null) ? String.Empty : value;
# Usage:  1..10 | ?: {$_ -gt 5} {“Greater than 5;$_} {“Not greater than 5”;$_}
# —————————————————————————
set-alias ?: Invoke-Ternary -Option AllScope -Description “PSCX filter alias”
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse) 
{
   if (&$decider) { 
      &$ifTrue
   } else { 
      &$ifFalse 
   }
}

and then you can use that like this:

$total = ($quantity * $price ) * (?:  {$quantity -le 10} {.9} {.75})

This is the most close variant I have seen so far.



回答7:

PowerShell doesn't have support for inline ifs. You'll have to create your own function (as another answer suggests), or combine if/else statements on a single line (as another answer also suggests).