Operador ternário em PowerShell
por exemplo, na linguagem C, que suporta o operador ternário, eu poderia escrever algo como:
<condition> ? <condition-is-true> : <condition-is-false>;
Se isso não existe realmente em PowerShell, qual seria a melhor maneira (ou seja, fácil de ler e manter) de alcançar o mesmo resultado?
10 answers
$result = If ($condition) {"true"} Else {"false"}
Tudo o resto é complexidade incidental e, portanto, deve ser evitado.
Para utilização em ou como expressão, não apenas uma atribuição, embrulhe-a em $()
, assim:
write-host $(If ($condition) {"true"} Else {"false"})
A construção de Powershell mais próxima que consegui arranjar para imitar é:
. ({'condition is false'},{'condition is true'})[$condition]
De acordo com este PowerShell blog post , pode criar um nome alternativo para definir um ?:
operador:
set-alias ?: Invoke-Ternary -Option AllScope -Description "PSCX filter alias"
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse)
{
if (&$decider) {
&$ifTrue
} else {
&$ifFalse
}
}
Usa-o assim:
$total = ($quantity * $price ) * (?: {$quantity -le 10} {.9} {.75})
Eu também, procurei uma resposta melhor, e enquanto a solução no post de Edward é "ok", eu cheguei com uma solução muito mais natural {[[4]}Neste post no blog
Curto e doce:
# ---------------------------------------------------------------------------
# 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."
O que torna mais fácil fazer coisas como (mais exemplos no post do blog):
$message == ($age > 50) ? "Old Man" :"Young Dude"
Uma vez que um operador ternário é normalmente usado ao atribuir valor, ele deve retornar um valor. Esta é a maneira que pode funcionar:
$var=@("value if false","value if true")[[byte](condition)]
Estúpido, mas a trabalhar. Também esta construção pode ser usada para transformar rapidamente um int em outro valor, basta adicionar elementos de array e especificar uma expressão que retorna valores não-negativos baseados em 0.
$var = @{$true="this is true";$false="this is false"}[1 -eq 1]
O mais feio de todos !
Tente a declaração de powershell como uma alternativa, especialmente para a atribuição de variáveis - múltiplas linhas, mas legível.
Exemplo,
$WinVer = switch ( Test-Path $Env:windir\SysWOW64 ) {
$true { "64-bit" }
$false { "32-bit" }
}
"This version of Windows is $WinVer"
O PowerShell não tem actualmente um nativo Se (ou ternário se ) mas pode considerar a utilização do cmdlet personalizado:
IIf <condition> <condition-is-true> <condition-is-false>
Ver: PowerShell Inline If (IIf)
Melhorei recentemente (Open PullRequest) os operadores ternários condicionais e nulos de coalescing na PoweShell lib 'Pscx'
O Pls procura a minha solução.
o meu ramo temático github: UtilityModule_Invoke-Operators
Funções:
Invoke-Ternary
Invoke-TernaryAsPipe
Invoke-NullCoalescing
NullCoalescingAsPipe
Outros nomes
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"
Utilização
<condition_expression> |?: <true_expression> <false_expression>
<variable_expression> |?? <alternate_expression>
Como expressão pode passar:
$null, um literal, uma variável, uma expressão ' externa '($b-eq 4) ou um scriptblock {$b-eq 4}
Se uma variável na expressão da variável é $null ou não existente, a expressão alternativa é avaliada como saída.
Aqui está uma abordagem alternativa de função personalizada:
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'
Exemplo
1 -eq 1 |??? 'match' : 'nomatch'
1 -eq 2 |??? 'match' : 'nomatch'
Diferenças Explicadas
- Por que são 3 pontos de interrogação em vez de 1?
- o carácter
?
já é um pseudónimo deWhere-Object
. -
??
é usado em outras línguas como um operador de coalescing nulo, e eu queria evitar confusão. - já que estou a utilizar o pipeline para avaliar isso, ainda precisamos deste personagem para canalizar a condição para a nossa função
- obtemos um resultado para cada valor; isto é
-2..2 |??? 'match' : 'nomatch'
dá:match, match, nomatch, match, match
(isto é, uma vez que qualquer int não-zero avalia paratrue
; enquanto que o zero avalia parafalse
). - Se você não quiser isso, converta o array para um bool;
([bool](-2..2)) |??? 'match' : 'nomatch'
(ou simplesmente:[bool](-2..2) |??? 'match' : 'nomatch'
)