Alles, was Sie über $null wissen wollten

Die PowerShell $null scheint oft einfach zu sein, hat aber viele Nuancen. Werfen wir einen engen Blick darauf $null , damit Sie wissen, was passiert, wenn Sie unerwartet in einen $null Wert geraten.

Hinweis

Die Originalversion dieses Artikels erschien auf dem Blog, der von @KevinMarquettegeschrieben wurde. Das PowerShell-Team danke Kevin für das Teilen dieser Inhalte mit uns. Bitte schauen Sie sich seinen Blog bei PowerShellExplained.coman.

Was ist NULL?

Sie können sich NULL als unbekannter oder leerer Wert vorstellen. Eine Variable ist NULL, bis Sie ihm einen Wert oder ein Objekt zuweisen. Dies kann wichtig sein, da einige Befehle vorhanden sind, die einen Wert erfordern und Fehler generieren, wenn der Wert NULL ist.

PowerShell-$null

$null ist eine automatische Variable in PowerShell, die verwendet wird, um NULL darzustellen. Sie können sie Variablen zuweisen, in Vergleichen verwenden und als Platzhalter für NULL in einer Auflistung verwenden.

PowerShell behandelt $null ein Objekt mit dem Wert NULL. Dies unterscheidet sich von dem, was Sie erwarten können, wenn Sie aus einer anderen Sprache kommen.

Beispiele für $null

Wenn Sie versuchen, eine Variable zu verwenden, die Sie nicht initialisiert haben, lautet $nullder Wert . Dies ist eine der gängigsten Möglichkeiten, wie sich $null-Werte in Ihren Code einschleichen.

PS> $null -eq $undefinedVariable
True

Wenn Sie einen Variablennamen falsch eingeben, sieht PowerShell ihn als andere Variable und den Wert .$null

Eine andere Möglichkeit ist, dass $null-Werte von anderen Befehlen stammen, die keine Ergebnisse liefern.

PS> function Get-Nothing {}
PS> $value = Get-Nothing
PS> $null -eq $value
True

Auswirkungen von $null

$null Werte wirken sich je nachdem, wo der Code angezeigt wird, unterschiedlich aus.

In Zeichenfolgen

Wenn Sie in einer Zeichenfolge verwenden $null , handelt es sich um einen leeren Wert (oder eine leere Zeichenfolge).

PS> $value = $null
PS> Write-Output "'The value is $value'"
'The value is '

Dies ist einer der Gründe, warum ich Klammern um Variablen platzieren möchte, wenn sie in Protokollnachrichten verwendet werden. Es ist noch wichtiger, die Ränder Ihrer Variablenwerte zu identifizieren, wenn sich der Wert am Ende der Zeichenfolge befindet.

PS> $value = $null
PS> Write-Output "The value is [$value]"
The value is []

Dadurch lassen sich leere Zeichenfolgen und $null Werte leicht erkennen.

In numerischen Gleichungen

Wenn ein $null Wert in einer numerischen Formel verwendet wird, sind die Ergebnisse ungültig, wenn sie keinen Fehler geben. Manchmal wird $null mit 0 ausgewertet, und in anderen Fällen ist das gesamte Ergebnis $null. Hier ist ein Beispiel mit Multiplikation, das 0 oder $null je nach Reihenfolge der Werte angibt.

PS> $null * 5
PS> $null -eq ( $null * 5 )
True

PS> 5 * $null
0
PS> $null -eq ( 5 * $null )
False

Anstelle einer Sammlung

Mit einer Auflistung können Sie einen Index verwenden, um auf Werte zuzugreifen. Wenn Sie versuchen, eine Auflistung zu indizieren, die tatsächlich nullist, erhalten Sie diesen Fehler: Cannot index into a null array.

PS> $value = $null
PS> $value[10]
Cannot index into a null array.
At line:1 char:1
+ $value[10]
+ ~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

Wenn Sie über eine Sammlung verfügen, aber versuchen, auf ein Element zuzugreifen, das sich nicht in der Auflistung befindet, erhalten Sie ein $null Ergebnis.

$array = @( 'one','two','three' )
$null -eq $array[100]
True

Anstelle eines Objekts

Wenn Sie versuchen, auf eine Eigenschaft oder Untereigenschaft eines Objekts zuzugreifen, das nicht über die angegebene Eigenschaft verfügt, erhalten Sie einen $null Wert wie für eine nicht definierte Variable. In diesem Fall spielt es keine Rolle, ob die Variable $null oder ein tatsächliches Objekt ist.

PS> $null -eq $undefined.Some.Fake.Property
True

PS> $date = Get-Date
PS> $null -eq $date.Some.Fake.Property
True

Methode für einen Ausdruck mit NULL-Wert

Das Aufrufen einer Methode auf einem $null Objekt führt zu einem RuntimeException.

PS> $value = $null
PS> $value.ToString()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $value.ToString()
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Wann immer ich den Ausdruck You cannot call a method on a null-valued expression sehe, dann suche ich zuerst nach Orten, an denen ich eine Methode für eine Variable aufrufe, ohne es $nullzuerst zu überprüfen.

Überprüfung auf $null

Vielleicht haben Sie bemerkt, dass ich in meinen Beispielen $null immer links platziere, wenn ich $null überprüfe. Dies ist beabsichtigt und wird als bewährte Methode für PowerShell akzeptiert. Es gibt einige Situationen, in denen das Positionieren auf der rechten Seite nicht das erwartete Ergebnis erzielt.

Sehen Sie sich dieses nächste Beispiel an, und versuchen Sie, die Ergebnisse vorherzusagen:

if ( $value -eq $null )
{
    'The array is $null'
}
if ( $value -ne $null )
{
    'The array is not $null'
}

Wenn ich $value nicht definiere, wird der erste zu $true ausgewertet und die Botschaft lautet The array is $null. Die Falle ist, dass ein $value erstellt werden kann, bei dem beide Ausdrücke als $false ausgewertet werden.

$value = @( $null )

In diesem Fall ist $value ein Array, das $null enthält. Der -eq prüft jeden Wert im Array und gibt den übereinstimmenden $null zurück. Dies wird als $false ausgewertet. -ne gibt alles zurück, was nicht mit $null übereinstimmt, und in diesem Fall gibt es keine Ergebnisse (Dies ergibt ebenfalls $false). Keines der beiden ist $true, auch wenn es so aussieht, als sollte dies auf eines zutreffen.

Nicht nur können wir einen Wert erstellen, der beide auf $false auswertet, es ist möglich, einen Wert zu erstellen, bei dem beide auf $true auswerten. Matthias Jessen (@IISResetMe) hat einen guten Beitrag , der sich in dieses Szenario eintaucht.

PSScriptAnalyzer und VS-Code

Das PSScriptAnalyzer-Modul verfügt über eine Regel, die dieses Problem prüft, genannt PSPossibleIncorrectComparisonWithNull.

PS> Invoke-ScriptAnalyzer ./myscript.ps1

RuleName                              Message
--------                              -------
PSPossibleIncorrectComparisonWithNull $null should be on the left side of equality comparisons.

Da VS Code auch die PSScriptAnalyser-Regeln verwendet, wird dies auch als Problem in Ihrem Skript hervorgehoben oder identifiziert.

Einfache if-Überprüfung

Eine häufige Methode, wie Personen nach einem Wert ohne $null suchen, besteht darin, eine einfache if() Anweisung ohne den Vergleich zu verwenden.

if ( $value )
{
    Do-Something
}

Wenn der Wert $null ist, ergibt dies $false. Dies ist leicht lesbar, aber achten Sie darauf, dass genau nach dem gesucht wird, wonach gesucht werden soll. Ich lese diese Codezeile wie:

Wenn $value einen Wert hat.

Aber das ist nicht die ganze Geschichte. Diese Zeile sagt eigentlich:

Wenn $value nicht $null oder 0 oder $false oder eine leere Zeichenfolge oder ein leeres Array ist.

Hier ist ein umfassenderes Beispiel für diese Aussage.

if ( $null -ne $value -and
        $value -ne 0 -and
        $value -ne '' -and
        ($value -isnot [array] -or $value.Length -ne 0) -and
        $value -ne $false )
{
    Do-Something
}

Es ist völlig in Ordnung, eine einfache if-Prüfung zu verwenden, solange Sie sich merken, dass diese anderen Werte als $false zählen und nicht nur, dass eine Variable einen Wert hat.

Ich bin vor ein paar Tagen beim Umstrukturieren von Code auf dieses Problem gestoßen. Er enthielt eine grundlegende Eigenschaftenüberprüfung wie diese.

if ( $object.Property )
{
    $object.Property = $value
}

Ich wollte der Objekteigenschaft nur dann einen Wert zuweisen, wenn er vorhanden ist. In den meisten Fällen hatte das ursprüngliche Objekt einen Wert, der in der $true-Anweisung zu if ausgewertet würde. Doch manchmal wurde der Wert nicht festgelegt. Ich habe den Code gedebuggt und festgestellt, dass das Objekt die Eigenschaft hatte, aber es war ein leerer Zeichenfolgenwert. Dadurch wurde verhindert, dass sie mit der bisherigen Logik aktualisiert wurde. Also habe ich eine richtige $null Prüfung hinzugefügt, und alles funktionierte.

if ( $null -ne $object.Property )
{
    $object.Property = $value
}

Kleine Fehler wie diese sind schwer zu erkennen und lassen mich akribisch Werte auf $null überprüfen.

$null. Zählen

Wenn Sie versuchen, auf eine Eigenschaft für einen $null Wert zuzugreifen, ist die Eigenschaft ebenfalls $null. Die Count Eigenschaft ist die Ausnahme von dieser Regel.

PS> $value = $null
PS> $value.Count
0

Wenn Sie einen $null Wert haben, dann ist Count0. Diese spezielle Eigenschaft wird von PowerShell hinzugefügt.

[PSCustom-Objekt] Zählen

Fast alle Objekte in PowerShell verfügen über diese Count Eigenschaft. Eine wichtige Ausnahme ist die [pscustomobject] in Windows PowerShell 5.1 (Dies ist in PowerShell 6.0 behoben). Es verfügt nicht über eine Count Eigenschaft, sodass Sie einen $null Wert erhalten, wenn Sie versuchen, sie zu verwenden. Ich erwähne das hier, damit Sie nicht versuchen, Count anstelle einer $null Prüfung zu verwenden.

Wenn Sie dieses Beispiel unter Windows PowerShell 5.1 und PowerShell 6.0 ausführen, erhalten Sie unterschiedliche Ergebnisse.

$value = [pscustomobject]@{Name='MyObject'}
if ( $value.Count -eq 1 )
{
    "We have a value"
}

Aufzählbare NULL

Es gibt eine spezielle Art davon $null , die anders wirkt als die anderen. Ich möchte es als enumerable NULL bezeichnen, aber es ist wirklich ein System.Management.Automation.Internal.AutomationNull. Diese aufzählbare Null ist das Ergebnis einer Funktion oder eines Skriptblocks, der nichts zurückgibt (ein leeres Ergebnis).

PS> function Get-Nothing {}
PS> $nothing = Get-Nothing
PS> $null -eq $nothing
True

Wenn Sie es mit $nullvergleichen, erhalten Sie einen $null Wert. Bei Verwendung in einer Auswertung, bei der ein Wert erforderlich ist, ist der Wert immer $null. Wenn Sie es jedoch in einem Array platzieren, wird es genauso behandelt wie ein leeres Array.

PS> $containEmpty = @( @() )
PS> $containNothing = @($nothing)
PS> $containNull = @($null)

PS> $containEmpty.Count
0
PS> $containNothing.Count
0
PS> $containNull.Count
1

Sie können ein Array haben, das einen $null Wert und dessen Count Wert 1enthält. Wenn Sie jedoch ein leeres Array in einem Array platzieren, wird es nicht als Element gezählt. Die Anzahl ist 0.

Wenn Sie die aufzählbare Null wie eine Sammlung behandeln, ist sie leer.

Wenn Sie eine aufzählbare NULL an einen Funktionsparameter übergeben, der nicht stark typisiert ist, wandelt PowerShell die aufzählbare NULL standardmäßig in einen $null-Wert um. Das bedeutet, dass der Wert innerhalb der Funktion als $null anstelle als Typ System.Management.Automation.Internal.AutomationNull behandelt wird.

Rohrleitung

Der primäre Ort, an dem der Unterschied angezeigt wird, ist die Verwendung der Pipeline. Sie können einen $null Wert, aber keinen aufzählbaren NULL-Wert weiter pipen.

PS> $null | ForEach-Object{ Write-Output 'NULL Value' }
'NULL Value'
PS> $nothing | ForEach-Object{ Write-Output 'No Value' }

Abhängig von Ihrem Code sollten Sie $null in Ihrer Logik berücksichtigen.

Überprüfen Sie in jedem Fall zuerst auf $null

  • Herausfiltern von NULL in der Pipeline (... | where {$null -ne $_} | ...)
  • Verarbeiten von NULL in der Pipelinefunktion

Foreach

Eines meiner bevorzugten Features von foreach ist, dass es keine Aufzählung für eine $null-Sammlung ausführt.

foreach ( $node in $null )
{
    #skipped
}

Dies erspart mir die Überprüfung der Sammlung auf $null, bevor ich eine Aufzählung für sie ausführe. Wenn Sie über eine Sammlung von $null-Werten verfügen, kann $node weiterhin $null sein.

foreach funktioniert so seit PowerShell 3.0. Wenn Sie eine ältere Version verwenden, ist dies nicht der Fall. Dies ist eine der wichtigen Änderungen, die beim Backporting von Code für die Kompatibilität von 2.0 beachtet werden müssen.

Werttypen

Technisch gesehen können nur Verweistypen $null werden. PowerShell ist jedoch sehr großzügig und ermöglicht, dass Variablen jeden beliebigen Typ haben können. Wenn Sie jedoch einen Werttyp strikt festlegen, kann er nicht $null werden. PowerShell konvertiert $null in einen Standardwert für viele Typen.

PS> [int]$number = $null
PS> $number
0

PS> [bool]$boolean = $null
PS> $boolean
False

PS> [string]$string = $null
PS> $string -eq ''
True

Für einige Typen gibt es keine gültige Konvertierung aus $null. Diese Typen generieren einen Cannot convert null to type Fehler.

PS> [datetime]$date = $null
Cannot convert null to type "System.DateTime".
At line:1 char:1
+ [datetime]$date = $null
+ ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

Funktionsparameter

Die Verwendung stark typierter Werte in Funktionsparametern ist sehr häufig. In der Regel lernen wir, die Typen unserer Parameter zu definieren, auch wenn wir in der Regel nicht die Typen anderer Variablen in unseren Skripts definieren. Möglicherweise verfügen Sie bereits über einige stark typierte Variablen in Ihren Funktionen, und sie werden nicht einmal erkannt.

function Do-Something
{
    param(
        [string] $Value
    )
}

Sobald Sie den Typ des Parameters als a stringfestlegen, kann der Wert niemals sein $null. Es ist üblich, zu überprüfen, ob ein Wert $null ist, um festzustellen, ob der Benutzer einen Wert bereitgestellt hat oder nicht.

if ( $null -ne $Value ){...}

$Value ist eine leere Zeichenfolge '' , wenn kein Wert angegeben wird. Verwenden Sie stattdessen die automatische Variable $PSBoundParameters.Value .

if ( $null -ne $PSBoundParameters.Value ){...}

$PSBoundParameters enthält nur die Parameter, die beim Aufrufen der Funktion angegeben wurden. Sie können auch die ContainsKey Methode verwenden, um die Eigenschaft zu prüfen.

if ( $PSBoundParameters.ContainsKey('Value') ){...}

IstNichtNullOderLeer

Wenn der Wert eine Zeichenfolge ist, können Sie eine statische Zeichenfolgenfunktion verwenden, um zu überprüfen, ob der Wert $null oder eine leere Zeichenfolge ist.

if ( -not [string]::IsNullOrEmpty( $value ) ){...}

Ich verwende dies oft, wenn ich weiß, dass der Werttyp ein String sein sollte.

Wann ich auf $null überprüfe

Ich bin ein defensiver Skripter. Wenn ich eine Funktion aufrufe und sie einer Variablen zuweist, prüfe ich sie auf $null.

$userList = Get-ADUser kevmar
if ($null -ne $userList){...}

Ich bevorzuge die Verwendung if oder foreach über die Verwendung try/catch. Verstehen Sie mich nicht falsch, ich verwende try/catch immer noch häufig. Wenn ich aber auf eine Fehlerbedingung oder eine leere Gruppe von Ergebnissen testen kann, kann ich die Ausnahmebehandlung für echte Ausnahmen zulassen.

Außerdem überprüfe ich gern auf $null, bevor ich einen Wert indiziere oder Methoden eines Objekts aufrufe. Diese beiden Aktionen schlagen für ein $null Objekt fehl, daher finde ich es wichtig, sie zuerst zu überprüfen. Ich habe diese Szenarien bereits weiter oben in diesem Beitrag behandelt.

Kein Ergebnisszenario

Es ist wichtig zu wissen, dass verschiedene Funktionen und Befehle das Szenario ohne Ergebnisse unterschiedlich behandeln. Viele PowerShell-Befehle geben den aufzählbaren Nullwert und einen Fehler im Fehlerdatenstrom zurück. Andere lösen jedoch Ausnahmen aus oder geben dir ein Statusobjekt. Es liegt noch an Ihnen, zu wissen, wie die Befehle, die Sie verwenden, mit Szenarien ohne Ergebnisse und mit Fehlern umgehen.

Initialisieren mit $null

Eine Gewohnheit, die ich aufgenommen habe, ist die Initialisierung aller meiner Variablen, bevor ich sie verwende. Sie müssen dies in anderen Sprachen tun. Am Anfang meiner Funktion oder während der Eingabe einer foreach-Schleife definiere ich alle Werte, die ich verwende.

Hier ist ein Szenario, bei dem ich möchte, dass Sie genauer hinschauen. Es ist ein Beispiel für einen Fehler, den ich nachverfolgen musste.

function Do-Something
{
    foreach ( $node in 1..6 )
    {
        try
        {
            $result = Get-Something -Id $node
        }
        catch
        {
            Write-Verbose "[$result] not valid"
        }

        if ( $null -ne $result )
        {
            Update-Something $result
        }
    }
}

Die Erwartung hier ist, dass Get-Something entweder ein Ergebnis oder ein aufzählbarer Nullwert zurückgegeben wird. Wenn ein Fehler auftritt, protokollieren wir ihn. Anschließend überprüfen wir, ob wir vor der Verarbeitung ein gültiges Ergebnis erhalten haben.

Der Fehler, der in diesem Code verborgen ist, tritt auf, wenn Get-Something eine Ausnahme auslöst und $result keinen Wert zugewiesen bekommt. Es schlägt vor der Zuordnung fehl, sodass wir $null der $result Variable nicht einmal zuweisen. $result enthält weiterhin den vorher gültigen $result-Wert aus anderen Iterationen. Update-Something wird in diesem Beispiel mehrfach für das gleiche Objekt ausgeführt.

Ich lege $result direkt innerhalb der $null-Schleife auf foreach fest, bevor ich es verwende, um dieses Problem zu vermeiden.

foreach ( $node in 1..6 )
{
    $result = $null
    try
    {
        ...

Geltungsbereichsprobleme

Dies trägt auch dazu bei, Bereichsprobleme zu vermeiden. In diesem Beispiel weisen wir $result immer wieder in einer Schleife Werte zu. Da PowerShell jedoch zulässt, dass Variablenwerte von außerhalb der Funktion in den Gültigkeitsbereich der aktuellen Funktion aufgenommen werden, werden durch ihre Initialisierung innerhalb Ihrer Funktion Fehler vermieden, die auf diese Weise eingeführt werden können.

Eine nicht initialisierte Variable in Ihrer Funktion ist nicht $null , wenn sie auf einen Wert in einem übergeordneten Bereich festgelegt ist. Der übergeordnete Bereich könnte eine andere Funktion sein, die Ihre Funktion aufruft und dieselben Variablennamen verwendet.

Wenn ich dasselbe Do-something Beispiel nehme und die Schleife entferne, würde ich mit etwas enden, das wie in diesem Beispiel aussieht:

function Invoke-Something
{
    $result = 'ParentScope'
    Do-Something
}

function Do-Something
{
    try
    {
        $result = Get-Something -Id $node
    }
    catch
    {
        Write-Verbose "[$result] not valid"
    }

    if ( $null -ne $result )
    {
        Update-Something $result
    }
}

Wenn der Aufruf von Get-Something eine Ausnahme auslöst, findet meine $null-Prüfung das $result aus Invoke-Something. Die Initialisierung des Werts in Ihrer Funktion entschärft dieses Problem.

Das Benennen von Variablen ist schwierig, und es ist üblich, dass ein Autor dieselben Variablennamen in mehreren Funktionen verwendet. Ich weiß, ich verwende $node,$result,$data immer. Daher wäre es sehr einfach, Werte aus verschiedenen Bereichen an Orten anzuzeigen, an denen sie nicht sein sollten.

Umleiten der Ausgabe zu $null

Ich habe im gesamten Artikel über $null-Werte gesprochen, aber das Thema ist nicht vollständig, wenn ich nicht das Umleiten der Ausgabe zu $null erwähne. Es gibt Situationen, in denen Sie Befehle haben, die Informationen oder Objekte ausgeben, die Sie unterdrücken möchten. Hierzu können Sie die Ausgabe zu $null umleiten.

Out-Null

Der Befehl Out-Null ist die integrierte Methode zum Umleiten von Pipelinedaten an $null.

New-Item -Type Directory -Path $path | Out-Null

Zuweisen zu $null

Sie können die Ergebnisse eines Befehls $null zuweisen, anstatt Out-Null zu verwenden.

$null = New-Item -Type Directory -Path $path

Da $null es sich um einen konstanten Wert handelt, können Sie ihn niemals überschreiben. Ich mag es nicht, wie es in meinem Code aussieht, aber es ist oft schneller als Out-Null.

Umleitung zu $null

Sie können auch den Umleitungsoperator verwenden, um die Ausgabe an $null zu senden.

New-Item -Type Directory -Path $path > $null

Wenn Sie mit in der Befehlszeile ausführbaren Dateien arbeiten, die in unterschiedlichen Datenströmen ausgeben. Sie können alle Ausgabestreams so zu $null umleiten:

git status *> $null

Zusammenfassung

Ich habe in diesem Artikel vieles angeschnitten und weiß, dass dieser Artikel fragmentierter ist als die meisten meiner eingehenden Abhandlungen. Das liegt daran, dass $null Werte an vielen verschiedenen Stellen in PowerShell angezeigt werden können, und alle Nuancen sind spezifisch für den Ort, an dem Sie sie finden. Ich hoffe, Sie nehmen daraus ein besseres Verständnis von $null und ein Bewusstsein für die komplizierteren Szenarien mit, auf die Sie möglicherweise stoßen könnten.