PowerShell 内联 If (IIF)

如何在 PowerShell 中创建带有内联 If (IIf,另见: 即时如果三元如果)的语句?

如果您还认为这应该是一个本地的 PowerShell 函数,请投赞成票: https://connect.microsoft.com/PowerShell/feedback/details/1497806/iif-statement-if-shorthand

最新情况(2019年10月7日)

微软连接已经退役了,但好消息是 支持 PowerShell (核心)语言中的三元运算符似乎已经在路上了..。

107030 次浏览

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

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

但是,由于在语法中添加了大量的括号和方括号,因此可以考虑使用以下 CmdLet (可能是现有的最小的 CmdLet 之一) :

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

这将简化您的命令:

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

增补2014-09-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 (由 {}包围) ,而不是一个对象,如果不需要它,它将不会被计算,如下例所示:

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-09-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-03-20:

使用三元运算符语法

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

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

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

例子 :

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

还有一种方法:

$condition = $false


"The condition is $(@{$true = "true"; $false = "false"}[$condition])"
'The condition is {0}.' -f ('false','true')[$condition]

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

Powershell 7支持 三元运算符三元运算符:

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

来自博客文章 DIY: 三元操作符:

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

这是我目前为止看到的最接近的变种。