Null coalescing in powershell

Esiste un operatore a coalescenza nulla in PowerShell?

Mi piacerebbe essere in grado di fare questi comandi c # in PowerShell:

var s = myval ?? "new value"; var x = myval == null ? "" : otherval; 

Non sono necessarie le estensioni della comunità PowerShell, è ansible utilizzare la dichiarazione standard di PowerShell se come espressione:

 variable = if (condition) { expr1 } else { expr2 } 

Quindi per le sostituzioni per la tua prima espressione è:

 var s = myval ?? "new value"; 

diventa uno dei seguenti (a seconda delle preferenze):

 $s = if ($myval -eq $null) { "new value" } else { $myval } $s = if ($myval -ne $null) { $myval } else { "new value" } 

oppure a seconda di cosa potrebbe contenere $ myval:

 $s = if ($myval) { $myval } else { "new value" } 

e la seconda espressione mappa in modo simile:

 var x = myval == null ? "" : otherval; 

diventa

 $x = if ($myval -eq $null) { "" } else { $otherval } 

Ora per essere onesti, questi non sono molto scattanti, e non sono neanche lontanamente confortevoli da usare come le forms C #.

Si potrebbe anche prendere in considerazione il wrapping in una funzione molto semplice per rendere le cose più leggibili:

 function Coalesce($a, $b) { if ($a -ne $null) { $a } else { $b } } $s = Coalesce $myval "new value" 

o possibilmente come, IfNull:

 function IfNull($a, $b, $c) { if ($a -eq $null) { $b } else { $c } } $s = IfNull $myval "new value" $myval $x = IfNull $myval "" $otherval 

Come puoi vedere una funzione molto semplice può darti un po ‘di libertà di syntax.

AGGIORNAMENTO: Un’opzione aggiuntiva da considerare nel mix è una funzione IsTrue più generica:

 function IfTrue($a, $b, $c) { if ($a) { $b } else { $c } } $x = IfTrue ($myval -eq $null) "" $otherval 

Quindi combinare la capacità di Powershell di dichiarare alias che assomigliano un po ‘agli operatori, si finisce con:

 New-Alias "??" Coalesce $s = ?? $myval "new value" New-Alias "?:" IfTrue $ans = ?: ($q -eq "meaning of life") 42 $otherval 

Chiaramente questo non sarà per il gusto di tutti, ma potrebbe essere quello che stai cercando.

Sì, PowerShell ha un vero operatore a coalescenza nulla o almeno un operatore che è capace di tale comportamento. Questo operatore è -ne :

 # Format: # ($a, $b, $c -ne $null)[0] ($null, 'alpha', 1 -ne $null)[0] # Output: alpha 

È un po ‘più versatile di un operatore a coalescenza nulla, poiché crea una matrice di tutti gli oggetti non nulli:

 $items = $null, 'alpha', 5, 0, '', @(), $null, $true, $false $instances = $items -ne $null [string]::Join(', ', ($instances | ForEach-Object -Process { $_.GetType() })) # Result: System.String, System.Int32, System.Int32, System.String, System.Object[], System.Boolean, System.Boolean 

-eq funziona in modo simile, utile per il conteggio delle voci nulle:

 ($null, 'a', $null -eq $null).Length # Result: 2 

Ma comunque, ecco un caso tipico per rispecchiare il C # ?? operatore:

 'Filename: {0}' -f ($filename, 'Unknown' -ne $null)[0] | Write-Output 

Spiegazione

Questa spiegazione è basata su un suggerimento di modifica da un utente anonimo. Grazie, chiunque tu sia!

In base all’ordine delle operazioni, questo funziona nell’ordine seguente:

  1. L’operatore , crea una serie di valori da testare.
  2. L’operatore -ne filtra tutti gli elementi dell’array che corrispondono al valore specificato – in questo caso, null. Il risultato è un array di valori non nulli nello stesso ordine dell’array creato nel passaggio 1.
  3. [0] è usato per selezionare il primo elemento dell’array filtrato.

Semplificando che:

  1. Crea un array di valori possibili, nell’ordine preferito
  2. Escludere tutti i valori nulli dalla matrice
  3. Prendi il primo object dall’array risultante

Avvertenze

A differenza dell’operatore a coalescenza nulla di C #, verrà valutata ogni espressione ansible, poiché il primo passo consiste nel creare un array.

Questa è solo una mezza risposta alla prima metà della domanda, quindi una quarta risposta se lo si desidera, ma esiste un’alternativa molto più semplice all’operatore null coalescing a condizione che il valore predefinito che si desidera utilizzare sia effettivamente il valore predefinito per il tipo :

 string s = myval ?? ""; 

Può essere scritto in PowerShell come:

 ([string]myval) 

O

 int d = myval ?? 0; 

si traduce in PowerShell:

 ([int]myval) 

Ho trovato il primo di questi utili durante l’elaborazione di un elemento xml che potrebbe non esistere e che, se esistesse, potrebbe avere uno spazio vuoto indesiderato attorno ad esso:

 $name = ([string]$row.td[0]).Trim() 

Il cast per la stringa protegge dall’elemento nullo e impedisce il rischio che Trim() non vada a buon fine.

Se si installa il modulo di estensione della comunità di Powershell, è ansible utilizzare:

?? è l’alias di Invoke-NullCoalescing.

 $s = ?? {$myval} {"New Value"} 

?: è l’alias di Invoke-Ternary.

 $x = ?: {$myval -eq $null} {""} {$otherval} 

$null, $null, 3 | Select -First 1

ritorna

3

 function coalesce { Param ([string[]]$list) #$default = $list[-1] $coalesced = ($list -ne $null) $coalesced[0] } function coalesce_empty { #COALESCE for empty_strings Param ([string[]]$list) #$default = $list[-1] $coalesced = (($list -ne $null) -ne '')[0] $coalesced[0] } 

Spesso trovo che devo anche trattare la stringa vuota come null quando si usa la coalesce. Ho finito per scrivere una funzione per questo, che usa la soluzione di Zenexer per coalescenza per la semplice null coalesce, e poi ho usato Keith Hill per il controllo vuoto o vuoto, e ho aggiunto che come una bandiera la mia funzione poteva fare entrambe le cose.

Uno dei vantaggi di questa funzione è che gestisce anche l’avere tutti gli elementi null (o vuoti), senza generare un’eccezione. Può anche essere utilizzato per molte variabili di input arbitrarie, grazie a come PowerShell gestisce gli input degli array.

 function Coalesce([string[]] $StringsToLookThrough, [switch]$EmptyStringAsNull) { if ($EmptyStringAsNull.IsPresent) { return ($StringsToLookThrough | Where-Object { $_ } | Select-Object -first 1) } else { return (($StringsToLookThrough -ne $null) | Select-Object -first 1) } } 

Questo produce i seguenti risultati del test:

 Null coallesce tests: 1 (w/o flag) - empty/null/'end' : 1 (with flag) - empty/null/'end' : end 2 (w/o flag) - empty/null : 2 (with flag) - empty/null : 3 (w/o flag) - empty/null/$false/'end' : 3 (with flag) - empty/null/$false/'end' : False 4 (w/o flag) - empty/null/"$false"/'end' : 4 (with flag) - empty/null/"$false"/'end' : False 5 (w/o flag) - empty/'false'/null/"$false"/'end': 5 (with flag) - empty/'false'/null/"$false"/'end': false 

Codice di prova:

 Write-Host "Null coalesce tests:" Write-Host "1 (w/o flag) - empty/null/'end' :" (Coalesce '', $null, 'end') Write-Host "1 (with flag) - empty/null/'end' :" (Coalesce '', $null, 'end' -EmptyStringAsNull) Write-Host "2 (w/o flag) - empty/null :" (Coalesce('', $null)) Write-Host "2 (with flag) - empty/null :" (Coalesce('', $null) -EmptyStringAsNull) Write-Host "3 (w/o flag) - empty/null/`$false/'end' :" (Coalesce '', $null, $false, 'end') Write-Host "3 (with flag) - empty/null/`$false/'end' :" (Coalesce '', $null, $false, 'end' -EmptyStringAsNull) Write-Host "4 (w/o flag) - empty/null/`"`$false`"/'end' :" (Coalesce '', $null, "$false", 'end') Write-Host "4 (with flag) - empty/null/`"`$false`"/'end' :" (Coalesce '', $null, "$false", 'end' -EmptyStringAsNull) Write-Host "5 (w/o flag) - empty/'false'/null/`"`$false`"/'end':" (Coalesce '', 'false', $null, "$false", 'end') Write-Host "5 (with flag) - empty/'false'/null/`"`$false`"/'end':" (Coalesce '', 'false', $null, "$false", 'end' -EmptyStringAsNull) 

Il più vicino che posso ottenere è: $s = $myval |?? "new value" $s = $myval |?? "new value"

Ho implementato l’ operatore di coalescenza nulla per quanto sopra in questo modo:

 function NullCoalesc { param ( [Parameter(ValueFromPipeline=$true)]$Value, [Parameter(Position=0)]$Default ) if ($Value) { $Value } else { $Default } } Set-Alias -Name "??" -Value NullCoalesc 

L’ operatore ternario condizionale potrebbe essere implementato in modo simile.

 function ConditionalTernary { param ( [Parameter(ValueFromPipeline=$true)]$Value, [Parameter(Position=0)]$First, [Parameter(Position=1)]$Second ) if ($Value) { $First } else { $Second } } Set-Alias -Name "?:" -Value ConditionalTernary 

E usato come: $x = ($myval -eq $null) |?: "" $otherval