ChatGPT解决这个技术问题 Extra ChatGPT

PowerShell 中的三元运算符

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

例如,在支持三元运算符的 C 语言中,我可以编写如下内容:

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

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

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

M
MikeSchinkel
$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 中最接近的。
与其他一些“非功能”答案不同,这也将确保只执行一个分支。
“其他一切都是偶然的复杂性,因此要避免。” - 意见,而不是事实。
“其他一切都是偶然的复杂性,因此要避免。” - 伟大的生活建议。
j
js2010

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

M
Maximilian Burszley

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

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

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

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

例子,

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

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

根据此 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 实心点。如果经常有用,自定义简洁是值得的。但如果这种情况很少发生,我会坚持使用“如果”表达。
G
Garrett Serack

我也在寻找更好的答案,虽然 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 > 50) ? "Old Man" :"Young Dude" # null 合并 $message =: $foo ?? "foo was empty" `
我知道,我做到了,我只是在评论我为什么要使用另一个别名。
佚名

从 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
V
Vesper

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

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

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


这将强制对左右选项进行急切评估:这与正确的三元运算有很大不同。
k
kris

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


s
sodawillow

由于我已经使用过很多次并且没有在此处列出,我将添加我的作品:

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

最丑陋的!

kinda source


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

我最近改进了(打开 PullRequest)PoweShell lib '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 或不存在,替代表达式被评估为输出。


i
iRon

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

IIf <condition> <condition-is-true> <condition-is-false>

请参阅:PowerShell inline If (IIf)


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

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

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


J
JohnLBevan

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

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 的别名。 ??在其他语言中用作空合并运算符,我想避免混淆。

这 ?字符已经是 Where-Object 的别名。

??在其他语言中用作空合并运算符,我想避免混淆。

为什么我们在命令之前需要管道?由于我正在使用管道来评估这个,我们仍然需要这个角色来将条件传递到我们的函数中

由于我正在使用管道来评估这个,我们仍然需要这个角色来将条件传递到我们的函数中

如果我传入一个数组会发生什么?我们得到每个值的结果;即-2..2 |??? 'match' : 'nomatch' 给出:match、match、nomatch、match、match(即因为任何非零 int 的计算结果为真;而零计算为假)。如果您不希望这样,请将数组转换为布尔值; ([bool](-2..2)) |??? 'match' : 'nomatch' (或者简单地说: [bool](-2..2) |??? 'match' : 'nomatch')

我们得到每个值的结果;即-2..2 |??? 'match' : 'nomatch' 给出:match、match、nomatch、match、match(即因为任何非零 int 的计算结果为真;而零计算为假)。

如果您不希望这样,请将数组转换为布尔值; ([bool](-2..2)) |??? 'match' : 'nomatch' (或者简单地说: [bool](-2..2) |??? 'match' : 'nomatch')