PowerShell内联If(IIf)

78
7个回答

121

你可以使用PowerShell的本地方法:

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

但是,由于这会给您的语法添加很多括号和方括号,因此您可以考虑使用以下CmdLet(可能是现有CmdLet中最小的之一):

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

这将简化您的命令,变为:
"The condition is " + (IIf $Condition "True" "False") + "."

添加于2014年9月19日:

我已经使用IIf命令一段时间了,我仍然认为在许多情况下它可以使语法更易读,但是我同意Jason提到的一个不想要的副作用,即明显只使用一个值时,两个可能的值都将被评估。因此,我对IIf命令进行了一些修改:

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

现在,您可以添加一个 ScriptBlock(用大括号{}包围)而不是对象,如果不需要该对象,则不会对其进行评估,如下面的示例所示:
IIf $a {1/$a} NaN

或者内联放置:

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

如果$a的值不为零,返回其乘法逆元;否则,将返回NaN(其中{1/$a}未被计算)。

另一个很好的例子是当您想要在一个可能为$Null的对象上运行方法时,它将使得一个非常模糊的语法变得简单易懂(特别是当您想要将其放置在一行内时)。

这种情况下,使用本地的“If”方式来实现,将会像这样:

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

(请注意,在需要重置 $a 的循环等情况中,Else 部分经常是必需的。)
(使用 IIf 命令时,它将如下所示:)
$a = IIf $Object {$Object.Method()}

(请注意,如果$Object$Null,并且没有提供$IfFalse值,则$a将自动设置为$Null。)
< p >添加于2014-09-19:

IIf cmdlet的微小更改,现在会设置当前对象($_$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}}
}

这意味着您可以使用对象的方法(PowerShell方式)简化语句,该对象可能为空$Null

现在其通用语法为$a = IIf $Object {$_.Method()}。一个更常见的例子将如下所示:

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

请注意,如果所涉及的注册表(HKCU:\Volatile Environment)不存在,则命令$VolatileEnvironment.GetValue("UserName")通常会导致"You cannot call a method on a null-valued expression."错误;而命令IIf $VolatileEnvironment {$_.GetValue("UserName")}则只会返回$Null
如果$If参数是一个条件(比如$Number -lt 5)或被强制转换为条件(采用[Bool]类型),IIf命令不会改变当前对象,例如:
$RegistryKeys | ForEach {
    $UserName = IIf ($Number -lt 5) {$_.GetValue("UserName")}
}

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

新增于2020-03-20:

使用三元运算符语法

PowerShell 7.0引入了一种新的语法,使用了三元运算符。它遵循C#三元运算符的语法:

三元运算符行为类似于简化版的if-else语句。 <condition>表达式被评估并将结果转换为布尔值,以确定下一步应该评估哪个分支:

如果<condition>表达式为真,则执行<if-true>表达式。 如果<condition>表达式为假,则执行<if-false>表达式。

示例:

"The multiplicative inverse of $a is $($a ? (& {1/$a}) : 'NaN')."

5
您的第一个示例可以稍微简化一下:“条件为$(If($Condition){"True"}Else{"False"}).” - Jason Shirk
5
如果这些值没有副作用,第二个示例看起来很好,但如果存在副作用则不好,因为在进入IIf函数之前,副作用会同时发生在$Right和$Wrong上。 - Jason Shirk

64

Powershell 7+ 允许使用 三元运算符

$price = 150
$text = $price -gt 100 ? 'expensive' : 'cheap'
$text
# output: 
# expensive

Powershell 6(或更早版本)会返回未被分配的值。

$price = 150
$text = if ($price -gt 100) { 'expensive' } else { 'cheap' }
$text
# output:
# expensive

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

2
mjolinor。在复合格式化中,那里巧妙地使用了索引运算符。 - John Bentley
不错。内联。一行代码。再加上一个小问题,看看这段代码是做什么的,让有兴趣的代码审查者来解决 :) - jim birch

5
这里还有另一种方法:
$condition = $false

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

不错,但它不会评估条件,因此在 $Condition = "Has Text"$Condition = 10 这样的情况下返回空值(有时可能是期望的,但不符合 PowerShell 的风格),因此我会将其更改为:"条件为 $(@("False", "True")[[Bool]$Condition])" - iRon
你是对的,就这个特定的例子而言。如果 $condition = 10,你可以这样做:"条件为 $(@{$true = "大于50"; $false = "小于50"}[$condition -gt 50])" - ojk

2

来自博客文章DIY:三元运算符

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 
   }
}

然后你可以像这样使用它:
$total = ($quantity * $price ) * (?:  {$quantity -le 10} {.9} {.75})

这是我迄今为止看过的最接近的变体。

0
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  

这只是上面讨论的自定义函数的重新发布/稍微改进。除了确定第二个参数是否传递了两个项目并且第三个参数没有传递外,没有什么新东西,将其拆分到两个参数中。这使它的行为方式与常见的VBA中的IIF一致。 - HeyNow

-1

PowerShell不支持内联if语句。您需要创建自己的函数(如另一个答案所建议的),或将if / else语句组合在单行上(如另一个答案也建议的)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接