Null verschmilzt in Powershell

115

Gibt es in Powershell einen Null-Koaleszenz-Operator?

Ich möchte diese c # -Befehle in Powershell ausführen können:

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

Antworten:

133

Powershell 7+

Powershell 7 führt in Powershell native Null-Koaleszenz-, Null-Bedingungszuweisungs- und ternäre Operatoren ein.

Null-Koaleszenz

$null ?? 100    # Result is 100

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

Null bedingte Zuweisung

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

Ternärer Operator

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

Vorherige Versionen:

Sie benötigen keine Powershell-Community-Erweiterungen. Sie können die Standard-Powershell-if-Anweisungen als Ausdruck verwenden:

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

Also zu den Ersetzungen für Ihren ersten C # -Ausdruck von:

var s = myval ?? "new value";

wird einer der folgenden (je nach Präferenz):

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

oder je nachdem, was $ myval enthalten könnte, könnten Sie verwenden:

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

und der zweite C # -Ausdruck wird auf ähnliche Weise abgebildet:

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

wird

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

Nun, um fair zu sein, diese sind nicht sehr bissig und bei weitem nicht so bequem zu bedienen wie die C # -Formulare.

Sie können es auch in eine sehr einfache Funktion einbinden, um die Lesbarkeit zu verbessern:

function Coalesce($a, $b) { if ($a -ne $null) { $a } else { $b } }

$s = Coalesce $myval "new value"

oder möglicherweise als IfNull:

function IfNull($a, $b, $c) { if ($a -eq $null) { $b } else { $c } }

$s = IfNull $myval "new value" $myval
$x = IfNull $myval "" $otherval

Wie Sie sehen, kann eine sehr einfache Funktion Ihnen einiges an Syntaxfreiheit geben.

UPDATE: Eine zusätzliche Option, die im Mix berücksichtigt werden muss, ist eine allgemeinere IsTrue-Funktion:

function IfTrue($a, $b, $c) { if ($a) { $b } else { $c } }

$x = IfTrue ($myval -eq $null) "" $otherval

Kombinieren Sie dann die Fähigkeit von Powershell, Aliase zu deklarieren, die ein bisschen wie Operatoren aussehen. Am Ende haben Sie:

New-Alias "??" Coalesce

$s = ?? $myval "new value"

New-Alias "?:" IfTrue

$ans = ?: ($q -eq "meaning of life") 42 $otherval

Natürlich wird dies nicht jedermanns Geschmack sein, aber es könnte das sein, wonach Sie suchen.

Wie Thomas bemerkt, besteht ein weiterer subtiler Unterschied zwischen der C # -Version und der obigen darin, dass C # die Argumente kurzschließt, aber die Powershell-Versionen, die Funktionen / Aliase enthalten, bewerten immer alle Argumente. Wenn dies ein Problem ist, verwenden Sie das ifAusdrucksformular.

StephenD
quelle
6
Das einzig wahre Äquivalent zum Koaleszenzoperator ist die Verwendung einer if-Anweisung. Das Problem ist, dass bei jedem anderen Ansatz alle Operanden ausgewertet werden, anstatt kurzzuschließen. "?? $ myval SomeReallyExpenisveFunction ()" ruft die Funktion auf, auch wenn $ myval nicht null ist. Ich nehme an, man könnte die Auswertung mit Scriptblocks verzögern, aber sei dir bewusst, dass Scriptblocks KEINE Abschlüsse sind und die Dinge klobig werden.
Thomas S. Trias
Funktioniert nicht im strengen Modus - es wirft The variable '$myval' cannot be retrieved because it has not been set..
BrainSlugs83
1
@ BrainSlugs83 Der Fehler, den Sie im strengen Modus sehen, hängt nicht mit den dargestellten Null-Koaleszenzoptionen zusammen. Es ist nur der Standard, bei dem Powershell überprüft, ob zuerst eine Variable definiert wird. Wenn Sie $myval = $nullvor dem Test festlegen , sollte der Fehler behoben sein.
StephenD
1
Warnung, Powershell-Eigenart, Null sollte immer (umständlich) verglichen werden, wobei Null an erster Stelle steht, dh, ich $null -ne $a kann jetzt keine anständige Referenz finden, ist aber eine langjährige Praxis.
DudeNumber4
90

PowerShell 7 und höher

PowerShell 7 führt viele neue Funktionen ein und migriert von .NET Framework zu .NET Core. Ab Mitte 2020 wurden ältere Versionen von PowerShell aufgrund der Abhängigkeit von .NET Core nicht vollständig ersetzt. Microsoft hat jedoch angegeben, dass die Core-Familie die ältere Framework-Familie ersetzen soll. Wenn Sie dies lesen, ist möglicherweise eine kompatible Version von PowerShell auf Ihrem System vorinstalliert. Wenn nicht, siehe https://github.com/powershell/powershell .

Gemäß der Dokumentation werden die folgenden Operatoren in PowerShell 7.0 sofort unterstützt:

  1. Null-Koaleszenz :??
  2. Null-Koaleszenz-Zuordnung :??=
  3. Ternär :... ? ... : ...

Diese funktionieren wie erwartet für eine Null-Koaleszenz:

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

Da ein ternärer Operator eingeführt wurde, ist jetzt Folgendes möglich, obwohl dies angesichts der Hinzufügung eines Null-Koaleszenzoperators nicht erforderlich ist:

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

Ab 7.0 ist auch Folgendes verfügbar, wenn die PSNullConditionalOperatorsoptionale Funktion aktiviert ist , wie in den Dokumenten ( 1 , 2 ) erläutert :

  1. Null-bedingter Mitgliederzugriff für Mitglieder: ?.
  2. Nullbedingter Mitgliederzugriff für Arrays et al.: ?[]

Diese haben ein paar Einschränkungen:

  1. Da diese experimentell sind, können sie sich ändern. Sie werden möglicherweise nicht mehr als experimentell angesehen, wenn Sie dies lesen, und die Liste der Vorbehalte hat sich möglicherweise geändert.
  2. Variablen müssen eingeschlossen werden, ${}wenn sie von einem der experimentellen Operatoren befolgt werden, da Fragezeichen in Variablennamen zulässig sind. Es ist unklar, ob dies der Fall sein wird, wenn / wenn die Merkmale den experimentellen Status verlassen (siehe Ausgabe Nr. 11379 ). Zum Beispiel ${x}?.Test()verwendet die neuen Betreiber, sondern $x?.Test()läuft Test()auf eine Variable mit dem Namen $x?.
  3. Es gibt keinen ?(Operator, wie Sie vielleicht erwarten, wenn Sie von TypeScript kommen. Folgendes wird nicht funktionieren:$x.Test?()

PowerShell 6 und früher

PowerShell-Versionen vor 7 haben einen tatsächlichen Null-Koaleszenz-Operator oder zumindest einen Operator, der zu einem solchen Verhalten fähig ist. Dieser Operator ist -ne:

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

# Output:
alpha

Es ist etwas vielseitiger als ein Null-Koaleszenz-Operator, da es ein Array aller Nicht-Null-Elemente erstellt:

$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 funktioniert ähnlich, was zum Zählen von Null-Einträgen nützlich ist:

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

# Result:
2

Aber hier ist ein typischer Fall, um den ??Operator von C # zu spiegeln :

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

Erläuterung

Diese Erklärung basiert auf einem Bearbeitungsvorschlag eines anonymen Benutzers. Danke, wer auch immer du bist!

Basierend auf der Reihenfolge der Operationen funktioniert dies in der folgenden Reihenfolge:

  1. Der ,Operator erstellt ein Array von zu testenden Werten.
  2. Der -neOperator filtert alle Elemente aus dem Array heraus, die dem angegebenen Wert entsprechen - in diesem Fall null. Das Ergebnis ist ein Array von Nicht-Null-Werten in derselben Reihenfolge wie das in Schritt 1 erstellte Array.
  3. [0] wird verwendet, um das erste Element des gefilterten Arrays auszuwählen.

Vereinfachung:

  1. Erstellen Sie ein Array möglicher Werte in der bevorzugten Reihenfolge
  2. Schließen Sie alle Nullwerte aus dem Array aus
  3. Nehmen Sie das erste Element aus dem resultierenden Array

Vorsichtsmaßnahmen

Im Gegensatz zum Null-Koaleszenz-Operator von C # wird jeder mögliche Ausdruck ausgewertet, da der erste Schritt darin besteht, ein Array zu erstellen.

Zenexer
quelle
Am Ende habe ich eine modifizierte Version Ihrer Antwort in meiner verwendet , weil ich zulassen musste, dass alle Instanzen in der Koaleszenz null sind, ohne eine Ausnahme auszulösen. Sehr schöne Art, es zu tun, hat mich viel gelernt. :)
Johny Skovdal
Diese Methode verhindert einen Kurzschluss. Definieren function DidItRun($a) { Write-Host "It ran with $a"; return $a }und ausführen ((DidItRun $null), (DidItRun 'alpha'), 1 -ne $null)[0], um dies zu sehen.
jpmc26
@ jpmc26 Ja, das ist beabsichtigt.
Zenexer
Jeder Versuch , eine coalesce zu definieren Funktion ist auch wahrscheinlich , um Kurzschlüsse zu beseitigen, nicht wahr?
Chris F Carroll
8
Ahhhh PowerShell's Operator Vorrang bringt mich um! Ich vergesse immer, dass der Kommaoperator ,eine so hohe Priorität hat. Für alle, die sich nicht sicher sind, wie das funktioniert, ($null, 'alpha', 1 -ne $null)[0]ist es dasselbe wie (($null, 'alpha', 1) -ne $null)[0]. Tatsächlich sind -splitund -join(und unäre Striche ? Dh -4oder -'56') die einzigen zwei "Strich" -Operatoren mit höherer Priorität .
Pluto
15

Dies ist nur eine halbe Antwort auf die erste Hälfte der Frage, also eine viertel Antwort, wenn Sie so wollen, aber es gibt eine viel einfachere Alternative zum Null-Koaleszenz-Operator, vorausgesetzt, der Standardwert, den Sie verwenden möchten, ist tatsächlich der Standardwert für den Typ ::

string s = myval ?? "";

Kann in Powershell geschrieben werden als:

([string]myval)

Oder

int d = myval ?? 0;

übersetzt in Powershell:

([int]myval)

Ich fand das erste davon nützlich, wenn ich ein XML-Element verarbeite, das möglicherweise nicht vorhanden ist und das, falls vorhanden, unerwünschte Leerzeichen enthält:

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

Der Cast-to-String schützt vor dem Null-Element und verhindert das Risiko eines Ausfalls Trim().

Duncan
quelle
([string]$null)-> ""scheint ein "nützlicher, aber unglücklicher Nebeneffekt" zu sein :(
user2864740
11

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

kehrt zurück

3

Chris F. Carroll
quelle
1
Der Kommentar von @ thomas-s-trias zum Kurzschluss ist jedoch weiterhin gültig.
Chris F Carroll
9

Wenn Sie das Powershell Community Extensions-Modul installieren, können Sie Folgendes verwenden:

?? ist der Alias ​​für Invoke-NullCoalescing.

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

?: ist der Alias ​​für Invoke-Ternary.

$x = ?: {$myval -eq $null} {""} {$otherval}
EBGreen
quelle
Eigentlich sind das keine PowerShell-Befehle. Sie haben sie zusammen mit pscx:?: -> Invoke-
Ternary
... tatsächlichen Code und Ergebnis verpasst ..;) Get-Command -Module pscx -CommandType alias | wo {$ _. Name -match '\?.' } | foreach {"{0}: {1}" -f $ _. Name, $ _. Definition}? :: Invoke-Ternary ?? : Invoke-NullCoalescing
BartekB
Ups ... du bist völlig richtig. Ich vergesse oft, dass ich dieses Modul sogar geladen habe.
EBGreen
5

Schließlich hat PowerShell 7 Null Coalescing-Zuweisungsoperatoren !

PS > $null ?? "a"
a
PS > "x" ?? "y"
x
PS > $x = $null
PS > $x ??= "abc"
PS > $x
abc
PS > $x ??= "xyz"
PS > $x
abc
Hiroki
quelle
3
@($null,$null,"val1","val2",5) | select -First 1
Statikrysis
quelle
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]
 }
Shawn
quelle
2

Oft stelle ich fest, dass ich leere Zeichenfolgen auch als Null behandeln muss, wenn ich Coalesce verwende. Am Ende habe ich eine Funktion dafür geschrieben, die Zenexers Lösung zum Zusammenführen für die einfache Null-Zusammenführung verwendet, und dann Keith Hills für die Null- oder Leerprüfung verwendet, und diese als Flag hinzugefügt, damit meine Funktion beides kann.

Einer der Vorteile dieser Funktion besteht darin, dass alle Elemente null (oder leer) sind, ohne eine Ausnahme auszulösen. Dank der Verarbeitung von Array-Eingaben durch PowerShell kann es auch für beliebig viele Eingabevariablen verwendet werden.

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

Dies führt zu folgenden Testergebnissen:

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

Testcode:

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)
Johny Skovdal
quelle
1

Am nächsten kann ich kommen: $Val = $MyVal |?? "Default Value"

Ich habe den Null-Koaleszenz-Operator für das Folgende wie folgt implementiert :

function NullCoalesc {
    param (
        [Parameter(ValueFromPipeline=$true)]$Value,
        [Parameter(Position=0)]$Default
    )

    if ($Value) { $Value } else { $Default }
}

Set-Alias -Name "??" -Value NullCoalesc

Der bedingte ternäre Operator könnte auf ähnliche Weise implementiert werden.

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

Und verwendet wie: $Val = $MyVal |?: $MyVal "Default Value"

Elon Mallin
quelle