Null coalescing in powershell


115

Esiste un operatore di coalescenza nullo in PowerShell?

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

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

Risposte:


133

Powershell 7+

Powershell 7 introduce la coalescenza null nativa, l'assegnazione condizionale null e gli operatori ternari in Powershell.

Null Coalescing

$null ?? 100    # Result is 100

"Evaluated" ?? (Expensive-Operation "Not Evaluated")    # Right side here is not evaluated

Assegnazione condizionale nulla

$x = $null
$x ??= 100    # $x is now 100
$x ??= 200    # $x remains 100

Operatore ternario

$true  ? "this value returned" : "this expression not evaluated"
$false ? "this expression not evaluated" : "this value returned"

Versione precedente:

Non sono necessarie le estensioni della community di Powershell, puoi utilizzare le istruzioni if ​​standard di Powershell come espressione:

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

Quindi, alle sostituzioni per la tua prima espressione C # di:

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

oa seconda di cosa potrebbe contenere $ myval potresti usare:

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

e la seconda espressione C # esegue la mappatura in modo simile:

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

diventa

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

Per essere onesti, questi non sono molto scattanti e non sono neanche lontanamente comodi da usare come i moduli C #.

Potresti anche considerare di avvolgerlo 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 eventualmente 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 sintassi.

AGGIORNAMENTO: un'opzione extra 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 questa è 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 tutti i gusti, ma potrebbe essere quello che stai cercando.

Come nota Thomas, un'altra sottile differenza tra la versione C # e quella precedente è che C # esegue il cortocircuito degli argomenti, ma le versioni di Powershell che coinvolgono funzioni / alias valuteranno sempre tutti gli argomenti. Se questo è un problema, usa il ifmodulo dell'espressione.


6
L'unico vero equivalente dell'operatore di coalescenza sta usando un'istruzione if; il problema è che qualsiasi altro approccio valuta tutti gli operandi invece di cortocircuitare. "?? $ myval SomeReallyExpenisveFunction ()" chiamerà la funzione anche se $ myval non è null. Suppongo che si possa ritardare la valutazione usando gli scriptblock, ma sii consapevole che gli scriptblock NON sono chiusure e le cose iniziano a diventare goffe.
Thomas S. Trias

Non funziona in modalità rigorosa: genera The variable '$myval' cannot be retrieved because it has not been set..
BrainSlugs83

1
@ BrainSlugs83 L'errore visualizzato in modalità rigorosa non è correlato alle opzioni di coalescenza nulle presentate. È solo lo standard, Powershell controlla che una variabile sia definita prima. Se si imposta $myval = $nullprima di eseguire il test, l'errore dovrebbe scomparire.
StephenD

1
Attenzione, stranezza di PowerShell, null dovrebbe sempre essere confrontato (goffamente) inserendo null per primo, cioè $null -ne $a Non riesco a trovare un riferimento decente ora, ma è una pratica di vecchia data.
dudeNumber4

90

PowerShell 7 e versioni successive

PowerShell 7 introduce molte nuove funzionalità e migra da .NET Framework a .NET Core. A partire dalla metà del 2020, non ha completamente sostituito le versioni legacy di PowerShell a causa della dipendenza da .NET Core, ma Microsoft ha indicato che intende che la famiglia Core sostituisca alla fine la famiglia Framework legacy. Nel momento in cui leggi questo, una versione compatibile di PowerShell potrebbe essere preinstallata sul tuo sistema; in caso contrario, vedere https://github.com/powershell/powershell .

Secondo la documentazione , i seguenti operatori sono supportati out-of-the-box in PowerShell 7.0:

  1. Null-coalescenza :??
  2. Assegnazione a coalescenza nulla :??=
  3. Ternario :... ? ... : ...

Funzionano come ci si aspetterebbe per la coalescenza nulla:

$x = $a ?? $b ?? $c ?? 'default value'
$y ??= 'default value'

Poiché è stato introdotto un operatore ternario, ora è possibile quanto segue, sebbene non sia necessario data l'aggiunta di un operatore di coalescenza nullo:

$x = $a -eq $null ? $b : $a

A partire dalla versione 7.0, sono disponibili anche i seguenti se la PSNullConditionalOperatorsfunzione opzionale è abilitata , come spiegato nei documenti ( 1 , 2 ):

  1. Accesso ai membri con condizioni nulle per i membri: ?.
  2. Accesso ai membri con condizioni nulle per array e altri: ?[]

Questi hanno alcuni avvertimenti:

  1. Poiché sono sperimentali, sono soggetti a modifiche. Potrebbero non essere più considerati sperimentali nel momento in cui leggerai questo articolo e l'elenco delle avvertenze potrebbe essere cambiato.
  2. Le variabili devono essere racchiuse tra ${}se seguite da uno degli operatori sperimentali perché i punti interrogativi sono consentiti nei nomi delle variabili. Non è chiaro se questo sarà il caso se / quando le funzionalità passeranno dallo stato sperimentale (vedi problema # 11379 ). Ad esempio, ${x}?.Test()utilizza l'operatore new, ma $x?.Test()viene eseguito Test()su una variabile denominata $x?.
  3. Non esiste un ?(operatore come ci si potrebbe aspettare se si proviene da TypeScript. Quanto segue non funzionerà:$x.Test?()

PowerShell 6 e versioni precedenti

Le versioni di PowerShell precedenti alla 7 hanno un operatore di coalescenza null effettivo o almeno un operatore in grado di eseguire tale comportamento. Quell'operatore è -ne:

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

# Output:
alpha

È un po 'più versatile di un operatore di coalescenza nullo, poiché crea un array di tutti gli elementi 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, il che è utile per contare le voci nulle:

($null, 'a', $null -eq $null).Length

# Result:
2

Comunque, ecco un caso tipico per rispecchiare l' ??operatore di C # :

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

Spiegazione

Questa spiegazione si basa su un suggerimento di modifica di un utente anonimo. Grazie, chiunque tu sia!

In base all'ordine delle operazioni, funziona nel seguente ordine:

  1. L' ,operatore crea un array di valori da testare.
  2. L' -neoperatore filtra tutti gli elementi dall'array che corrispondono al valore specificato, in questo caso null. Il risultato è una matrice di valori non nulli nello stesso ordine della matrice creata nel passaggio 1.
  3. [0] viene utilizzato per selezionare il primo elemento della matrice filtrata.

Semplificando che:

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

Avvertenze

A differenza dell'operatore di coalescenza null di C #, verrà valutata ogni possibile espressione, poiché il primo passaggio consiste nel creare un array.


Ho finito per utilizzare una versione modificata della tua risposta nella mia , perché dovevo consentire a tutte le istanze nella coalescenza di essere nulle, senza lanciare un'eccezione. Un modo molto carino di farlo, però, mi ha imparato molto. :)
Johny Skovdal

Questo metodo impedisce il cortocircuito. Definisci function DidItRun($a) { Write-Host "It ran with $a"; return $a }e corri ((DidItRun $null), (DidItRun 'alpha'), 1 -ne $null)[0]per vederlo.
jpmc26

@ jpmc26 Sì, è di progettazione.
Zenexer

È probabile che anche qualsiasi tentativo di definire una funzione di coalescenza elimini il cortocircuito, no?
Chris F Carroll

8
Ahhhh la precedenza degli operatori di PowerShell mi uccide! Mi dimentico sempre che l'operatore virgola ,ha una precedenza così alta. Per chiunque sia confuso su come funziona, ($null, 'alpha', 1 -ne $null)[0]è lo stesso di (($null, 'alpha', 1) -ne $null)[0]. Infatti gli unici due operatori "trattino" con maggiore precendenza sono -splite -join(e trattino unario? Ie . -4O -'56').
Plutone

15

Questa è solo una mezza risposta alla prima metà della domanda, quindi un quarto di risposta se vuoi, ma esiste un'alternativa molto più semplice all'operatore di coalescenza nullo a condizione che il valore predefinito che desideri 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 utile durante l'elaborazione di un elemento xml che potrebbe non esistere e che, se esistesse, potrebbe contenere spazi vuoti indesiderati attorno ad esso:

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

Il cast su stringa protegge dall'elemento null e previene qualsiasi rischio di Trim()errore.


([string]$null)-> ""sembra un "effetto collaterale utile ma sfortunato" :(
user2864740


9

Se installi il Powershell Community Extensions Module , puoi utilizzare:

?? è l'alias per Invoke-NullCoalescing.

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

?: è l'alias di Invoke-Ternary.

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

In realtà, non sono i comandi di PowerShell. Li hai messi insieme con pscx:?: -> Invoke-
Ternary

... codice e risultato effettivi mancati ..;) Get-Command -Module pscx -CommandType alias | dove {$ _. Name -match '\ ?.' } | foreach {"{0}: {1}" -f $ _. Name, $ _. Definition}?:: Invoke-Ternary ?? : Invoke-NullCoalescing
BartekB

Oops ... hai completamente ragione. Spesso dimentico che ho persino quel modulo di caricamento.
EBGreen



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

2

Spesso trovo che devo anche trattare la stringa vuota come nulla quando utilizzo il coalesce. Ho finito per scrivere una funzione per questo, che utilizza la soluzione di Zenexer per il coalescing per il semplice null coalesce, quindi ho usato Keith Hill per il controllo null o vuoto, aggiungendo quello come flag in modo che la mia funzione potesse fare entrambe le cose.

Uno dei vantaggi di questa funzione è che gestisce anche tutti gli elementi null (o vuoti), senza generare eccezioni. Può anche essere utilizzato per molte variabili di input arbitrarie, grazie al modo in cui 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)

1

Il più vicino che posso ottenere è: $Val = $MyVal |?? "Default Value"

Ho implementato l' operatore di coalescenza nullo 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: $Val = $MyVal |?: $MyVal "Default Value"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.