【问题标题】:PowerShell inline If (IIf)PowerShell 内联 If (IIf)
【发布时间】:2014-10-30 04:59:54
【问题描述】:

如何在 PowerShell 中创建带有内联 If(IIf,另请参见:Immediate ifternary If)的语句?

如果你也认为这应该是原生 PowerShell 函数,请投票:https://connect.microsoft.com/PowerShell/feedback/details/1497806/iif-statement-if-shorthand

更新(2019 年 10 月 7 日)

Microsoft Connect 已退役,但好消息是 support for a ternary operator in PowerShell (Core) language 似乎即将推出...

【问题讨论】:

  • 最后一个链接(实际上)已损坏:“Microsoft Connect 已停用”
  • 维基百科的文章说“立即如果”,而不是“内联如果”
  • @Peter Mortensen,感谢 cmets,我已将它们相应地纳入问题中。

标签: powershell iif


【解决方案1】:

Powershell 7 允许ternary operators:

$message = (Test-Path $path) ? "Path exists" : "Path not found"

早期版本:PowerShell 会返回尚未分配的值。

$a = if ($condition) { $true } else { $false }

例子:

# Powershell 7 or later
"The item is $( $price -gt 100 ? 'expensive' : 'cheap' )"

# Powershell 6 or earlier
"The item is $(if ($price -gt 100) { 'expensive' } else { 'cheap' })"

让我们试试吧:

$price = 150
The item is expensive
$price = 75
The item is cheap

【讨论】:

    【解决方案2】:

    您可以使用 PowerShell 的原生方式:

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

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

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

    这会将您的命令简化为:

    "The condition is " + (IIf $Condition "True" "False") + "."
    

    于 2014 年 9 月 19 日添加:

    我使用IIf cmdlet 已经有一段时间了,我仍然认为它在很多情况下会使语法更具可读性,但我同意 Jason 关于不想要的副作用的说明,即两个可能的值都会即使很明显只使用了一个值,我也对IIf cmdlet 进行了一些更改:

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

    现在您可能添加一个 ScriptBlock(由 {}'s 包围)而不是一个对象,如果不需要,则不会被评估,如本例所示:

    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}
    

    (请注意,Else 部分通常在需要重置 $a 的循环中是必需的。)

    使用IIf cmdlet 将如下所示:

    $a = IIf $Object {$Object.Method()}
    

    (请注意,如果$Object$Null,如果没有提供$IfFalse 值,$a 将自动设置为$Null。)


    于 2014 年 9 月 19 日添加:

    对现在设置当前对象($_$PSItem)的 IIf cmdlet 进行细微更改:

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

    这意味着您可以使用可能是$Null 的对象上的方法来简化语句(PowerShell 方式)。

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

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

    请注意,如果相关注册表 (HKCU:\Volatile Environment) 不存在,命令$VolatileEnvironment.GetValue("UserName") 通常会导致“您不能在空值表达式上调用方法。”错误;其中命令IIf $VolatileEnvironment {$_.GetValue("UserName")} 只会返回$Null

    如果$If 参数是条件(类似于$Number -lt 5)或强制条件(具有[Bool] 类型),则IIf cmdlet 不会覆盖当前对象,例如:

    $RegistryKeys | ForEach {
        $UserName = IIf ($Number -lt 5) {$_.GetValue("UserName")}
    }
    

    或者:

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

    添加于 2020 年 3 月 20 日:

    Using the ternary operator syntax

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

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

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

    示例

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

    【讨论】:

    • 你的第一个例子可以稍微简单一些:“条件是 $(If($Condition) {"True"} Else {"False"})。”
    • 第二个例子很好,如果值没有副作用,但如果有副作用就不好了,因为在进入 IIf 函数之前,副作用会同时发生在 $Right 和 $Wrong 上。跨度>
    【解决方案3】:

    来自博文DIY: Ternary operator

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

    这是迄今为止我见过的最接近的变种。

    【讨论】:

      【解决方案4】:
      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  
      

      【讨论】:

      • 这只是对上面讨论的自定义功能的重新发布/轻微改进。除了确定是否为第二个参数传递了两个项目而第三个参数没有传递的小块之外,没有什么真正新的东西,将其拆分为两个参数。这使它的行为方式与 IIF 常见的 VBA 一致。
      【解决方案5】:

      PowerShell 不支持内联 if。您必须创建自己的函数(如另一个答案所示),或将 if/else 语句组合在一行中(如另一个答案所示)。

      【讨论】:

        【解决方案6】:
        'The condition is {0}.' -f ('false','true')[$condition]
        

        【讨论】:

        • mjolinor。巧妙地使用索引运算符(结合复合格式)。
        • 不错。排队。一条线。加上一些代码对感兴趣的代码审查者有什么问题:)
        【解决方案7】:

        这是另一种方式:

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

        【讨论】:

        • 很好,但它不评估条件,因此在 $Condition = "Has Text"$Condition = 10 之类的情况下不返回任何内容(有时可能需要,但不是 PowerShell 之类的),因此我会将其更改为:"The condition is $(@("False", "True")[[Bool]$Condition])"
        • 你是对的,对于这个具体的例子。如果 $condition = 10,你会这样做:“条件是 $(@{$true = "大于 50"; $false = "小于 50"}[$condition -gt 50])"跨度>
        猜你喜欢
        • 2020-01-08
        • 1970-01-01
        • 2013-06-05
        • 2020-05-21
        • 2017-10-28
        • 2011-06-26
        • 2019-03-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多