PowerShell中的三元运算符


214

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

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

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

如果PowerShell中确实不存在这种情况,那么达到相同结果的最佳方法(即易于阅读和维护)是什么?


5
看看github.com/nightroman/PowerShellTraps/tree/master/Basic/…。如果这是您想要的,我可以为您解答。
罗曼·库兹敏

2
它是条件运算符或if。它不是“三元运算符”,因为这意味着一个带有三个参数的运算符(任何运算符)。
Damien_The_Unbeliever 2015年

7
@Damien_The_Unbeliever从技术上讲这是正确的,但通常称为三元运算符。“由于该运算符通常是该语言中唯一存在的三元运算符,因此有时有时简称为“三元运算符”。在某些语言中,该运算符称为“条件运算符”。 三元运算
mguassa

Visual Basic没有真正的三元运算符,但认为IF和IFF在功能上等效。
马特

@Matt这是不正确的。该IIF 函数将始终对两个操作数求值。该If声明将不-看到stackoverflow.com/questions/1220411/...:(新VB.NET版本中添加的三元表达式If (x, y, z)
user2864740

Answers:


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

其他所有事情都是附带的复杂性,因此应避免。

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

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

3
它在等号的右侧起作用,但并不完全符合您期望的三元运算符-会失败:“ a” + If($ condition){“ true”}其他{“ false”}“ a” + (If($ condition){“ true”}其他{“ false”}) 这有效(我不确定为什么):“ a” + $(If($ condition){“ true”}其他{“ false “})
Lamarth's

12
@Lamarth-之所以起作用,是因为$()包装器将语句的评估作为表达式强制进行,从而返回了false值的真实值,就像三元运算符一样。这是您在PowerShell AFAIK中获得的最接近的结果。
KeithS

4
与其他一些“非功能”的答案,这确保只有一个分支被执行。
user2864740

63

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

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

15
({true}, {false})[!$condition](也许)稍微好一点:a)传统的真假部分顺序;b)$condition不必只是0或1或$ false,$ true。操作员!根据需要对其进行转换。即$condition可以是42:!42〜$ false〜0〜第一个表达式。
Roman Kuzmin 2015年

1
不完全相同,@ RomanKuzmin。mjolinor的示例返回一个字符串。但是,将他的示例中插入到表达式中的相同字符串值返回一个脚本块。:-(
Michael Sorens 2015年

5
通过{true}{false}我的意思是<true expression><false expression>,没有脚本块。很抱歉,不准确。
Roman Kuzmin

1
感谢您的澄清@RomanKuzmin-现在,我在您的建议中看到了价值。
Michael Sorens 2015年

12
这将强制对左右选项进行急切评估:这与适当的三元运算有很大不同。
user2864740

24

根据此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})

似乎没有理由认为“决定者”是一个脚本块。如果?:曾经被评估过,它将要求对参数进行评估。没有脚本块,它将具有类似的用法,例如。(?: ($quantity -le 10) {.9} {.75})
user2864740 '18

这是一个很酷的功能,但是它可以将脚本呈现为非标准脚本,例如,除非别名定义也与之一起移植,否则您的脚本或脚本的某些部分可能无法移植。此时,您基本上是在创建自己的子语言。由于增加的复杂性和降低的可读性,所有这些都可能导致维护成本增加。
Vance McCorkle

1
@VanceMcCorkle实心点。如果经常使用习惯,简洁是值得的。但是,如果这种情况很少发生,我会坚持使用“ if”表达式。
爱德华·布雷


21

我也一直在寻找更好的答案,尽管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" 

1
这真棒。我只是不喜欢=用作别名,因为==在C#和许多其他语言中用于相等性检查。在Powersheller中,有一些C#的经验非常普遍,这会使生成的代码有些混乱。:可能是更好的别名。将其与变量赋值结合使用=:可能会使某人想起Pascal中使用的赋值,但在VB.NET和C#中没有任何立即等效项(据我所知)。
nohwnd

您可以将其设置为您想要的任何别名。: ~任何使您的船浮起的东西。:D set-alias : Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment." #三元$message =: ($age > 50) ? "Old Man" :"Young Dude" #空合并$message =: $foo ?? "foo was empty"`
Garrett Serack

我知道,而且确实如此,我只是在评论为什么要使用另一个别名。
nohwnd

15

尝试使用powershell的switch语句作为替代方案,尤其是对于变量分配-多行,但可读。

例,

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

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

9

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

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

愚蠢但有效。同样,此构造可用于快速将int转换为另一个值,只需添加数组元素并指定一个返回基于0的非负值的表达式即可。


6
这将强制对左右选项进行急切评估:这与适当的三元运算有很大不同。
user2864740

6

由于我已经使用了很多次,并且没有在此处看到它,因此我将添加我的文章:

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

最丑的!

有点来源


4
这将强制对左右选项进行急切评估:这与适当的三元运算有很大不同。
user2864740

@ user2864740,这是因为PS中没有适当的三元操作。这是一个合成的变体。
维戈隆德

2
@ViggoLundén缺少适当的三元运算符不会降低注释的正确性。这种“合成变异” 具有不同的行为
user2864740 '19

您是对的,在重新阅读您的评论后,我了解了它如何能够带来真正的改变。谢谢。
sodawillow

5

我最近改进了(打开PullRequest)PoweShell lib'Pscx'Pls中的三元条件和null运算符,
以寻找我的解决方案。


我的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或不存在,替代表达式被评估为输出。


4

PowerShell当前没有本地Inline If(或三元If),但是您可以考虑使用自定义cmdlet:

IIf <condition> <condition-is-true> <condition-is-false>
请参阅:PowerShell Inline If(IIf)


该链接后显示了如何创建一个IIf功能。但是,没有标准的PowerShell IIf功能/ cmdlet。
user2864740

1
@ user2864740,那又如何呢?是否将答案排除为以下问题的可能可用答案:“ 达到相同结果的最佳方法(即易于阅读和维护)是什么?” 但是,除此之外,该链接指向的IIf 问题(发布于14年9月5日)与该问题非常相似(重复?)。链接的问题中的其余答案也可以看作是附加值(如果没有,主要是因为它们是重复的)。
iRon

进行一点点解释很长的路要走,这就是为什么不鼓励裸露的链接的原因,如果没有其他附加信息,也不会关闭重复链接。考虑到很小的解释将大大提高这种裸链接答案的质量:“ Powershell不支持直接的“三元”(^但请参见其他答案)。但是,可以编写诸如(^ using这种方法,或查看其他答案)..”,但这不是要添加的工作。答案可以修改/更新等。
user2864740 '18

@ user2864740,感谢您的反馈,我对答案做了相应的调整。
iRon

1

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

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'

1

如果您只是在寻找一种基于布尔条件分配/返回字符串或数字的语法上简单的方法,则可以使用乘法运算符,如下所示:

"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”等将不起作用。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.