PowerShell中的三元运算符

372

据我所知,PowerShell似乎没有所谓的三元操作符的内置表达式。

例如,在支持三元操作符的C语言中,我可以写出以下代码:

<condition> ? <condition-is-true> : <condition-is-false>;

如果PowerShell中真的不存在这个功能,那么实现同样的结果最好的方式是什么(即易于阅读和维护)?

6
请查看 https://github.com/nightroman/PowerShellTraps/tree/master/Basic/Missing-ternary-operator 。如果这是你要找的内容,我可以将其作为答案。 - Roman Kuzmin
2
这是一个条件运算符或三元if。它不是"三元运算符",因为这只是指一个(任何)接受三个参数的运算符。 - Damien_The_Unbeliever
10
那在技术上是正确的,但通常被称为三元运算符。"由于这个运算符经常是该语言中唯一存在的三元运算符,因此有时简称为“三元运算符”。在某些语言中,此运算符被称为“条件运算符”。" 三元运算符 - mguassa
Visual Basic没有真正的三元运算符,但认为IF和IFF在功能上是等效的。 - Matt
4
PowerShell 7 版本中,添加了三元运算符作为原生功能。据此提供了相应答案。 - user189198
显示剩余2条评论
15个回答

499
$result = If ($condition) {"true"} Else {"false"}

如果要将其用作表达式而非仅仅是赋值,需使用 $() 将其包裹起来,例如:

write-host  $(If ($condition) {"true"} Else {"false"}) 

3
这适用于等号右侧,但三元运算符的使用方法与预期不太相同 —— 这些会失败:*"a" + If ($condition) {"true"} Else {"false"}* 和 "a" + (If ($condition) {"true"} Else {"false"}) 。而这个则可行(我还不确定原因):*"a" + $(If ($condition) {"true"} Else {"false"})*。 - Lamarth
20
@Lamarth - 这个方法有效是因为 $() 包装器强制将语句作为表达式进行求值,从而返回真值或假值,就像三元运算符一样。据我所知,这是在 PowerShell 中最接近的实现方式。 - KeithS
6
与其他一些“非功能性”答案不同,这种方法还可以确保仅执行一个分支。 - user2864740
3
“Everything else is incidental complexity and thus to be avoided.” - 观点,不是事实。 (意思为:其他一切都是次要的复杂性,因此应避免。) - TimTheEnchanter
6
“其它所有的都是无关紧要的复杂性,因此应该避免。” - 这是一条很好的人生建议。 - Max Cascone
如果您的“真实”值评估为空集合,那么这种方法将失败,并且会被强制转换为null。 - undefined

160

3
啊,太酷了!我希望现在OOTB服务器上更普遍地使用PS7! - Alex Kwitny

85

我能想到的最接近模拟它的PowerShell结构是:

@({'condition is false'},{'condition is true'})[$condition]

26
也许({true}, {false})[!$condition]更好一些:a)真和假的部分遵循传统顺序;b)$condition不必只是0或1或$false、$true。运算符!会根据需要将其转换。也就是说,$condition可以是任何值,比如42:!42 ~ $false ~ 0 ~ 第一个表达式。 - Roman Kuzmin
2
不完全相同,@RomanKuzmin。 mjolinor的示例返回一个字符串。但是从他的示例中提取的相同字符串值插入到您的表达式中会返回一个脚本块。 :-( - Michael Sorens
6
我的意思是,通过“{true}”和“{false}”,我指的是<true expression>和<false expression>,而不是脚本块。抱歉没有表达清楚。 - Roman Kuzmin
1
感谢您的澄清,@RomanKuzmin--现在我明白了您建议的价值。 - Michael Sorens
18
这将强制左右选项进行急切评估:这与适当的三元操作有很大不同。 - user2864740
显示剩余8条评论

34

尝试使用PowerShell的切换语句作为替代方案,特别是用于变量赋值-多行但易读。

例如,

$WinVer = switch ( Test-Path -Path "$Env:windir\SysWOW64" ) {
  $true    { "64-bit" }
  $false   { "32-bit" }
}
"This version of Windows is $WinVer"

2
这可能会稍微冗长一些,但与其他答案相比有显著的优势。特别是,它没有依赖于外部代码,也没有依赖于将函数复制/粘贴到脚本或模块中。 - bshacklett

27
根据这篇PowerShell博客文章,您可以创建一个别名来定义?:操作符:
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})

似乎没有理由将“decider”作为脚本块。如果?:被评估,它将需要对参数进行评估。如果没有脚本块,它将具有类似的用法,例如(?: ($quantity -le 10) {.9} {.75}) - user2864740
3
这是一个很酷的功能,但它可能会使您的脚本变得非标准化,例如,除非别名定义也随之移植,否则您的脚本或其中的部分可能无法移植。在这一点上,您基本上正在创建自己的子语言。所有这些都可能导致维护成本增加,因为复杂性增加而可读性降低。 - Vance McCorkle
1
@VanceMcCorkle 说得好。如果经常有用,自定义的简洁性是值得付出代价的。但如果情况很少发生,我会坚持使用“if”表达式。 - Edward Brey

24
自 PowerShell 7 版本开始,三元运算符已内置于 PowerShell 中。
1 -gt 2 ? "Yes" : "No"
# Returns "No"

1 -gt 2 ? 'Yes' : $null
# Get a $null response for false-y return value

谢谢。有什么办法可以将“否”语句替换为“null”或其他内容吗? - Ruslan Korkin
1
@RuslanKorkin 我刚刚更新了答案,加上了一个示例。你可以使用 $null - user189198
重复的问题。请参考https://stackoverflow.com/a/58271485/11635。 - undefined

23
我也在寻找更好的答案,虽然Edward帖子中的解决方案“可以”,但我在这篇博客文章中提出了一个更自然的解决方案
简洁明了:
# ---------------------------------------------------------------------------
# Name:   Invoke-Assignment
# Alias:  =
# Author: Garrett Serack (@FearTheCowboy)
# Desc:   Enables expressions like the C# operators: 
#         Ternary: 
#             <condition> ? <trueresult> : <falseresult> 
#             e.g. 
#                status = (age > 50) ? "old" : "young";
#         Null-Coalescing 
#             <value> ?? <value-if-value-is-null>
#             e.g.
#                name = GetName() ?? "No Name";
#             
# Ternary Usage:  
#         $status == ($age > 50) ? "old" : "young"
#
# Null Coalescing Usage:
#         $name = (get-name) ? "No Name" 
# ---------------------------------------------------------------------------

# returns the evaluated value of the parameter passed in, 
# executing it, if it is a scriptblock   
function eval($item) {
    if( $item -ne $null ) {
        if( $item -is "ScriptBlock" ) {
            return & $item
        }
        return $item
    }
    return $null
}

# an extended assignment function; implements logic for Ternarys and Null-Coalescing expressions
function Invoke-Assignment {
    if( $args ) {
        # ternary
        if ($p = [array]::IndexOf($args,'?' )+1) {
            if (eval($args[0])) {
                return eval($args[$p])
            } 
            return eval($args[([array]::IndexOf($args,':',$p))+1]) 
        }

        # null-coalescing
        if ($p = ([array]::IndexOf($args,'??',$p)+1)) {
            if ($result = eval($args[0])) {
                return $result
            } 
            return eval($args[$p])
        } 

        # neither ternary or null-coalescing, just a value  
        return eval($args[0])
    }
    return $null
}

# alias the function to the equals sign (which doesn't impede the normal use of = )
set-alias = Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment."

这使得像以下这样的操作变得容易(更多示例请参阅博客文章):
$message == ($age > 50) ? "Old Man" :"Young Dude" 

4
这真是太棒了。我只是不喜欢使用=作为别名,因为在C#和许多其他语言中用于相等性检查的是==。在PowerShell中拥有一些C#经验是非常普遍的,这使得结果代码有点令人困惑。:可能是一个更好的别名。与变量赋值=:一起使用可能会让某人想起Pascal中使用的赋值,但在VB.NET和C#中没有任何直接等价物(据我所知)。 - nohwnd
你可以将其设置为任何别名,例如 :~ 或者其他你喜欢的。:Dset-alias : Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment."

三元运算符

$message =: ($age > 50) ? "老头子" :"年轻小伙子"

空值合并运算符

$message =: $foo ?? "foo 为空"
- Garrett Serack
我知道,我也这么做了,只是在评论为什么我会使用另一个别名。 - nohwnd

10

由于三目运算符通常用于赋值操作,因此它应该返回一个值。以下是实现该功能的方式:

$var=@("value if false","value if true")[[byte](condition)]

有些愚蠢,但却起作用。此外,这种构造可以用于快速将整数转换为另一个值,只需添加数组元素并指定返回基于0的非负值的表达式即可。


7
这将强制左右选项进行急切评估:这与适当的三元操作非常不同。 - user2864740

9
PowerShell中的三元运算符是在PowerShell 7.0版本中引入的。
[Condition] ? (output if True) : (output if False)

示例 01

$a = 5; $b = 6
($a -gt $b) ? "True" : "False"

输出

False

例子 02

($a -gt $b) ? ("$a is greater than $b") : ("$a is less than $b")

输出

5 is less than 6

更多信息https://www.tutorialspoint.com/how-ternary-operator-in-powershell-works

(此链接为介绍PowerShell中三元运算符的文章)

6
我最近在 PowerShell 库“Pscx”中改进了三元条件运算符和空值合并运算符(通过开放 PullRequest)。请查看我的解决方案。
我的 GitHub 主题分支链接如下:UtilityModule_Invoke-Operators
以下是我新增的函数:
Invoke-Ternary
Invoke-TernaryAsPipe
Invoke-NullCoalescing
NullCoalescingAsPipe

别名

Set-Alias :?:   Pscx\Invoke-Ternary                     -Description "PSCX alias"
Set-Alias ?:    Pscx\Invoke-TernaryAsPipe               -Description "PSCX alias"
Set-Alias :??   Pscx\Invoke-NullCoalescing              -Description "PSCX alias"
Set-Alias ??    Pscx\Invoke-NullCoalescingAsPipe        -Description "PSCX alias"

使用方法

<condition_expression> |?: <true_expression> <false_expression>

<variable_expression> |?? <alternate_expression>

作为表达式,您可以传递以下内容:
$null,字面量,变量,'外部'表达式($b -eq 4)或脚本块{$b -eq 4}

如果变量表达式中的变量为$null或不存在,则评估替代表达式作为输出。

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