【问题标题】:Ternary operator in PowerShellPowerShell 中的三元运算符
【发布时间】:2015-09-29 06:36:38
【问题描述】:

据我所知,PowerShell 似乎没有所谓的ternary operator 的内置表达式。

例如,在支持三元运算符的 C 语言中,我可以这样写:

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

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

【问题讨论】:

  • 看看github.com/nightroman/PowerShellTraps/tree/master/Basic/…。如果这是您正在寻找的内容,我可以将其作为答案。
  • 它是一个条件运算符或三元if。这不是“三元运算符”,因为这意味着一个运算符(any 运算符),它接受三个参数。
  • @Damien_The_Unbeliever 这在技术上是正确的,但它通常被称为三元运算符。 “由于该运算符通常是语言中唯一存在的三元运算符,因此有时简称为“三元运算符”。在某些语言中,该运算符称为“条件运算符”。 Ternary operation
  • Visual basic 没有真正的三元运算符,但认为 IF 和 IFF 在功能上是等效的。
  • 在版本 7 中将三元运算符添加到本机 PowerShell。相应地添加了答案。

标签: powershell ternary-operator conditional-operator


【解决方案1】:
$result = If ($condition) {"true"} Else {"false"}

用于或用作表达式,而不仅仅是赋值,请将其包装在 $() 中,因此:

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

【讨论】:

  • 这适用于等号的右侧,但与您期望的三元运算符不同 - 这些失败:"a" + If ($condition) {"true"} Else {"false"}"a" + (If ($condition) {"true"} Else {"false"}) 这行得通(我还不确定为什么): "a" + $(If ($condition) {"true"} Else {"false"})
  • @Lamarth - 这是因为$() 包装器强制将语句评估为表达式,从而返回假值的真值,就像预期的三元运算符一样。这是您在 PowerShell AFAIK 中最接近的。
  • 与其他一些“非功能”答案不同,这将确保只执行一个分支。
  • “其他一切都是偶然的复杂性,因此要避免。” - 意见,而不是事实。
  • “其他一切都是偶然的复杂性,因此要避免。” - 伟大的生活建议。
【解决方案2】:

PowerShell 中的三元运算符是在 PowerShell version7.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

【讨论】:

    【解决方案3】:

    尝试使用 powershell 的 switch 语句作为替代,特别是对于变量赋值 - 多行,但可读。

    例子,

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

    【讨论】:

    • 这可能会稍微冗长一些,但与许多其他答案相比有很大的好处。特别是,不依赖于外部代码,也不依赖于复制/粘贴到脚本或模块中的函数。
    【解决方案4】:

    PowerShell 目前没有 没有有原生的Inline If(或ternary If),但您可以考虑使用自定义 cmdlet:

    IIf <i>&lt;condition&gt;</i> <i>&lt;condition-is-true&gt;</i> <i>&lt;condition-is-false&gt;</i>

    见:PowerShell inline If (IIf)

    【讨论】:

    • 链接的帖子显示了如何创建 IIf 函数。但是,没有标准的 PowerShell IIf function/cmdlet。
    • @user2864740,那又怎样?这是否排除了答案作为问题的可能可用答案:“什么是完成相同结果的最佳方式(即易于阅读和维护)?”但除此之外,链接指的是 IIf question(2014 年 9 月 5 日发布)very 与此类似(重复?)。链接问题中的其余答案也可以视为附加值(如果没有,主要是因为它们是重复的)。
    • 一点点解释就大有帮助,这就是为什么不鼓励裸链接,如果没有额外的附加信息,也不会关闭重复的链接。考虑到一个非常小的解释会显着提高这样一个 bare link 答案的质量:“Powershell 不支持直接的'三元'(^ 但请参阅其他答案)。但是,可以写一个函数,例如(^使用此方法,或查看其他答案)..”,但这不是要添加的 my 工作。答案可以根据需要修改/更新/等。
    • @user2864740,感谢您的反馈,我已对答案进行了相应的调整。
    【解决方案5】:

    从 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”或其他东西替换“No”语句吗?
    • @RuslanKorkin 我刚刚用一个例子更新了答案。你可以使用$null
    【解决方案6】:

    如果您只是在寻找一种语法上简单的方法来根据布尔条件分配/返回字符串或数字,您可以像这样使用乘法运算符:

    "Condition is "+("true"*$condition)+("false"*!$condition)
    (12.34*$condition)+(56.78*!$condition)
    

    如果您只对结果为真时的结果感兴趣,则可以完全省略错误部分(反之亦然),例如一个简单的评分系统:

    $isTall = $true
    $isDark = $false
    $isHandsome = $true
    
    $score = (2*$isTall)+(4*$isDark)+(10*$isHandsome)
    "Score = $score"
    # or
    # "Score = $((2*$isTall)+(4*$isDark)+(10*$isHandsome))"
    

    请注意,布尔值不应是乘法中的前导项,即 $condition*"true" 等将不起作用。

    【讨论】:

      【解决方案7】:

      Powershell 7 拥有它。 https://toastit.dev/2019/09/25/ternary-operator-powershell-7/

      PS C:\Users\js> 0 ? 'yes' : 'no'
      no
      PS C:\Users\js> 1 ? 'yes' : 'no'
      yes
      

      【讨论】:

        【解决方案8】:

        我能想出的最接近的 PowerShell 构造是:

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

        【讨论】:

        • ({true}, {false})[!$condition] 稍微好一点(也许):a)真假部分的传统顺序; b) $condition 不必只是 0 或 1 或 $false、$true。运算符! 根据需要对其进行转换。 IE。 $condition 可以是 42: !42 ~ $false ~ 0 ~ 第一个表达式。
        • 不完全相同,@RomanKuzmin。 mjolinor 的示例返回一个字符串。但是插入到您的表达式中的示例中的相同字符串值会返回一个脚本块。 :-(
        • {true}{false} 我的意思是 &lt;true expression&gt;&lt;false expression&gt;,而不是脚本块。抱歉不准确。
        • 感谢@RomanKuzmin 的澄清——现在我看到了您的建议的价值。
        • 这将强制对左右选项进行急切评估:这与正确的三元运算有很大不同。
        【解决方案9】:

        我最近改进了(打开 PullRequest)PoweShell 库“Pscx”中的三元条件和空值合并运算符
        请看看我的解决方案。


        我的 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 或不存在,则将替代表达式计算为输出。

        【讨论】:

          【解决方案10】:

          这是另一种自定义函数方法:

          function Test-TernaryOperatorCondition {
              [CmdletBinding()]
              param (
                  [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
                  [bool]$ConditionResult
                  ,
                  [Parameter(Mandatory = $true, Position = 0)]
                  [PSObject]$ValueIfTrue
                  ,
                  [Parameter(Mandatory = $true, Position = 1)]
                  [ValidateSet(':')]
                  [char]$Colon
                  ,
                  [Parameter(Mandatory = $true, Position = 2)]
                  [PSObject]$ValueIfFalse
              )
              process {
                  if ($ConditionResult) {
                      $ValueIfTrue
                  }
                  else {
                      $ValueIfFalse
                  }
              }
          }
          set-alias -Name '???' -Value 'Test-TernaryOperatorCondition'
          

          示例

          1 -eq 1 |??? 'match' : 'nomatch'
          1 -eq 2 |??? 'match' : 'nomatch'
          

          差异说明

          • 为什么是 3 个问号而不是 1 个?
            • ? 字符已经是Where-Object 的别名。
            • ?? 在其他语言中用作空合并运算符,我想避免混淆。
          • 为什么我们在命令之前需要管道?
            • 由于我使用管道来评估它,我们仍然需要这个字符来将条件通过管道传递到我们的函数中
          • 如果我传入一个数组会发生什么?
            • 我们得到每个值的结果;即-2..2 |??? 'match' : 'nomatch' 给出:match, match, nomatch, match, match(即,因为任何非零 int 计算结果为 true;而零计算结果为 false)。
            • 如果您不想这样,请将数组转换为布尔值; ([bool](-2..2)) |??? 'match' : 'nomatch'(或简称:[bool](-2..2) |??? 'match' : 'nomatch'

          【讨论】:

            【解决方案11】:

            我也在寻找更好的答案,虽然 Edward 帖子中的解决方案是“好的”,但我想出了一个更自然的 solution in this blog post

            又短又甜:

            # ---------------------------------------------------------------------------
            # 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" 
            

            【讨论】:

            • 这太棒了。我只是不喜欢使用= 作为别名,因为== 在C# 和许多其他语言中用于相等性检查。拥有一些 C# 经验在 powershellers 中很常见,这使得生成的代码有点混乱。 : 可能是一个更好的别名。将它与变量赋值 =: 结合使用可能会让人想起 Pascal 中使用的赋值,但在 VB.NET 或 C# 中没有任何直接等效(我知道的)。
            • 您可以将其设置为您想要的任何别名。 : ~ 或任何漂浮在您船上的东西。 :D set-alias : Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment." # 三元 $message =: ($age &gt; 50) ? "Old Man" :"Young Dude" # 空合并 $message =: $foo ?? "foo was empty" `
            • 我知道,我做到了,我只是在评论我为什么要使用另一个别名。
            【解决方案12】:

            根据这个PowerShell blog post,您可以创建一个别名来定义?: 运算符:

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

            【讨论】:

            • 似乎没有理由认为“决策者”是一个脚本块。如果 ?: 被评估过,则需要评估参数。如果没有脚本块,它将具有类似的用法,例如。 (?: ($quantity -le 10) {.9} {.75})
            • 这是一个很酷的功能,但它可以将您的脚本呈现为非标准,例如除非别名定义也随它一起移植,否则您的脚本或其部分可能无法移植。此时您基本上是在创建自己的子语言。由于复杂性增加和可读性降低,所有这些都可能导致维护成本增加。
            • @VanceMcCorkle 实心点。如果经常有用,自定义简洁是值得的。但如果这种情况很少发生,我会坚持使用“如果”表达式。
            【解决方案13】:

            因为我已经用过很多次了,在这里没有看到它,我将添加我的一块:

            $var = @{$true="this is true";$false="this is false"}[1 -eq 1]

            最丑陋的!

            kinda source

            【讨论】:

            • 这将强制对左右选项进行急切评估:这与正确的三元运算有很大不同。
            • @user2864740 那是因为 PS 中没有正确的三元运算。这是一个合成变体。
            • @ViggoLundén 缺少适当的三元运算符并不会降低注释的正确性。这种“合成变体”具有不同的行为
            • 您是对的,在重新阅读您的评论后,我明白它如何产生真正的影响。谢谢。
            【解决方案14】:

            由于赋值时通常使用三元运算符,因此它应该返回一个值。这是可行的方式:

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

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

            【讨论】:

            • 这将强制对左右选项进行急切评估:这与正确的三元运算有很大不同。
            猜你喜欢
            • 1970-01-01
            • 2021-10-06
            • 2021-08-01
            • 2018-09-10
            • 2017-03-09
            相关资源
            最近更新 更多