Minden, amit tudni akart $null

A PowerShell $null gyakran egyszerűnek tűnik, de sok árnyalata van. Vessünk egy pillantást $null , hogy tudjuk, mi történik, ha váratlanul belefut egy $null értékbe.

Megjegyzés:

A cikk eredeti verziója@KevinMarquette blogján jelent meg. A PowerShell csapata köszönjük Kevinnek, hogy megosztotta velünk ezt a tartalmat. Kérjük, látogasd meg a blogot a PowerShellExplained.comoldalon.

Mi az a NULL?

A NULL érték ismeretlen vagy üres értékként is felfogható. A változó null értékű, amíg hozzá nem rendel egy értéket vagy objektumot. Ez azért lehet fontos, mert vannak olyan parancsok, amelyekhez értékre van szükség, és hibaüzeneteket generálnak, ha az érték NULL.

PowerShell-$null

$null egy automatikus változó a PowerShellben, amely a NULL értéket jelöli. Hozzárendelheti a változókhoz, összehasonlításban használhatja, és a NULL helyőrzőjeként használhatja egy gyűjteményben.

A PowerShell null értékű objektumként kezeli $null . Ez eltér attól, amit elvárhat, ha más nyelvről származik.

Példák $null

Amikor olyan változót próbál használni, amelyet nem inicializált, az érték a következő $null. Ez az egyik leggyakoribb módszer, amellyel $null az értékek belopódnak a kódba.

PS> $null -eq $undefinedVariable
True

Ha véletlenül tévesen írja be a változó nevét, akkor a PowerShell más változóként látja, és az érték az $null.

A másik módszer az értékek megkeresése $null , ha más parancsokból származnak, amelyek nem adnak eredményt.

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

A $null hatása

$null az értékek eltérően befolyásolják a kódot attól függően, hogy hol jelennek meg.

Sztringekben

Ha sztringben használja $null , akkor az üres érték (vagy üres sztring).

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

Ez az egyik oka annak, hogy a változók köré szögletes zárójeleket szeretnék elhelyezni, amikor naplóüzenetekben használják őket. Még fontosabb azonosítani a változóértékek széleit, ha az érték a sztring végén van.

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

Így az üres sztringek és $null értékek könnyen felismerhetőek.

Numerikus egyenletben

Amikor egy $null értéket használunk egy numerikus egyenletben, az eredmények érvénytelenek lesznek, ha nem jeleznek hibát. Néha a $null kiértékelés eredménye 0 , máskor pedig a teljes eredmény $null. Íme egy példa a szorzásra, amely 0 értéket ad, vagy $null az értékek sorrendjétől függően.

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

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

Gyűjtemény helyett

A gyűjtemények segítségével index használatával érheti el az értékeket. Ha valójában nullegy gyűjteménybe próbál indexelni, a következő hibaüzenet jelenik meg: 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

Ha rendelkezik gyűjteménysel, de megpróbál hozzáférni egy olyan elemhez, amely nem szerepel a gyűjteményben, eredményt kap $null .

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

Objektum helyett

Ha olyan objektum tulajdonságához vagy altulajdonságához próbál hozzáférni, amely nem rendelkezik a megadott tulajdonságmal, egy olyan értéket kap $null , mint egy definiálatlan változó esetében. Ebben az esetben nem számít, hogy a $null változó vagy egy tényleges objektum.

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

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

Metódus null értékű kifejezésen

Egy $null objektumon egy metódus meghívása kivált egy 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

Amikor látom a kifejezést You cannot call a method on a null-valued expression , akkor az első dolog, amit keresek, azok a helyek, ahol egy metódust hívok meg egy változón anélkül, hogy először ellenőrizném $null.

$null ellenőrzése

Lehet, hogy észrevette, hogy mindig a $null-t helyezem bal oldalra, amikor ellenőrzöm a $null-t a példáimban. Ez szándékos, és a PowerShell ajánlott eljárása. Vannak olyan helyzetek, amikor a jobb oldalon való elhelyezés nem adja meg a várt eredményt.

Tekintse meg ezt a következő példát, és próbálja meg előrejelezni az eredményeket:

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

Ha nem határozom meg $value, az első értéke $true és az üzenetünk az The array is $null. Itt az a csapda, hogy létrehozható egy $value olyan, amely lehetővé teszi, hogy mindkettő $false

$value = @( $null )

Ebben az esetben a $value tömb egy .$null A -eq tömb minden értékét ellenőrzi, és a $null megfeleltetés értékét adja vissza. Ez kiértékelődik: $false. A -ne visszaad mindent, ami nem egyezik $null-vel, és ebben az esetben nincsenek eredmények (Ez a kifejezés $false-re van kiértékelve). Egyik sem $true noha úgy tűnik, hogy az egyiknek lennie kellene.

Nem csak olyan értéket hozhatunk létre, amely mindkettő kiértékelését $false-ra eredményezi, hanem olyan értéket is lehet létrehozni, amelynél mindkettő kiértékelése $true-re történik meg. Mathias Jessen (@IISResetMe) ír egy jó bejegyzést, amely részletezi ezt a forgatókönyvet.

PSScriptAnalyzer és VS Code

A PSScriptAnalyzer modul rendelkezik egy szabálysal, amely ellenőrzi ezt a problémát.PSPossibleIncorrectComparisonWithNull

PS> Invoke-ScriptAnalyzer ./myscript.ps1

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

Mivel a VS Code a PSScriptAnalyser-szabályokat is használja, a szkriptben ezt a problémát is kiemeli vagy azonosítja.

Egyszerű, ha van ellenőrzés

A nem $null értékek ellenőrzésének gyakori módja egy egyszerű if() állítás használata összehasonlítás nélkül.

if ( $value )
{
    Do-Something
}

Ha az érték az $null, akkor a kiértékelés eredménye a következő lesz $false: . Ez könnyen olvasható, de legyen óvatos, hogy pontosan azt keresi, amit elvár. Ezt a kódsort a következőképpen olvastam:

Ha $value van értéke.

De nem ez az egész történet. Ez a sor valójában a következőt mondja:

Ha $value nem $null , vagy 0$false üres sztring vagy üres tömb.

Íme egy teljesebb példa erre az utasításra.

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
}

Teljesen rendben van az alapszintű if ellenőrzés használata, amennyiben emlékszik arra, hogy a többi érték is számít $false, és nem csupán az, hogy egy változónak van értéke.

Néhány nappal ezelőtt egy kód újrabontásakor belefutottam ebbe a problémába. Ehhez hasonló alaptulajdonság-ellenőrzést kapott.

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

Csak akkor akartam értéket hozzárendelni az objektumtulajdonsághoz, ha létezik. A legtöbb esetben az eredeti objektum olyan értékkel rendelkezik, amely kiértékelhető $true az if utasításban. De belefutottam egy olyan problémába, ahol az érték időnként nem lett beállítva. Hibakeresést hajtottam végre a kódon, és megállapítottam, hogy az objektum rendelkezik a tulajdonsággal, de az üres karakterlánc értékként volt. Ez megakadályozta, hogy valaha is frissüljön az előző logikával. Ezért hozzáadtam egy megfelelő $null ellenőrzést, és minden működött.

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

Ez a kis hibák, mint ezek, amelyek nehéz észrevenni, és hogy én agresszíven ellenőrizze értékek .$null

$null.ElemekSzáma

Ha megpróbál hozzáférni egy tulajdonsághoz egy $null értéken, akkor a tulajdonság is $null. A Count tulajdonság kivételt képez a szabály alól.

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

Ha van egy $null érték, akkor a Count0. Ezt a speciális tulajdonságot a PowerShell hozzáadja.

[PSCustomObject] Szám

A PowerShell szinte minden objektuma rendelkezik ezzel a Count tulajdonságmal. Az egyik fontos kivétel a [pscustomobject] Windows PowerShell 5.1-ben (ez a PowerShell 6.0-ban van javítva). Count Nincs tulajdonsága, ezért ha használni próbálja, értéket kap$null. Kérem, hogy ne próbálja meg Count helyett $null ellenőrzést használni.

A példa Windows PowerShell 5.1-en és PowerShell 6.0-n való futtatása eltérő eredményeket ad.

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

Számba veendő null

Van egy speciális típus $null , amely másképp viselkedik, mint a többi. Számba veendő nullnak fogom nevezni, de valójában System.Management.Automation.Internal.AutomationNull. Ez a számba veendő null az a függvény vagy szkriptblokk eredménye, amely semmit (üres eredményt) ad vissza.

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

Ha összehasonlítja $null értékkel, $null értéket kap. Ha olyan kiértékelésben használják, ahol szükség van egy értékre, az érték mindig $null. Ha azonban egy tömbben helyezi el, az üres tömbként lesz kezelve.

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

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

Lehet egy olyan tömböd, amely egy $null értéket tartalmaz, és az Count1. Ha azonban üres tömböt helyez egy tömbbe, akkor az nem számít elemnek. A szám 0.

Ha a számozható nullt gyűjteményként kezeli, akkor üres.

Ha egy felsorolható null értéket ad át egy nem erősen típusos függvényparaméternek, a PowerShell alapértelmezés szerint a felsorolható null értéket egy $null értékké alakítja át. Ez azt jelenti, hogy a függvényen belül az érték a $null típus helyett lesz kezelve.

Csővezeték

Elsősorban a különbséget akkor látja, amikor a csővezetéket használja. Egy $null értéket továbbíthat, de nem egy felsorolható nullértéket.

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

A kódjától függően figyelembe kell venni a $null-t a logikájában.

Először ellenőrizze, hogy van-e $null.

  • Null szűrés a folyamaton (... | where {$null -ne $_} | ...)
  • Kezelés a folyamatfüggvényben

foreach

Az egyik kedvenc funkcióm foreach az, hogy nem iterál a $null gyűjteményen.

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

Így nem kell ellenőriznem $null a gyűjteményt, mielőtt enumerálnám. Ha $null értékgyűjteménnyel rendelkezik, akkor a $node még mindig lehet $null.

A foreach PowerShell 3.0-val kezdett így dolgozni. Ha történetesen egy régebbi verzión dolgozik, akkor ez nem igaz. Ez az egyik fontos változás, amelyet érdemes figyelembe venni a 2.0-s kompatibilitású kód visszahordásakor.

Értéktípusok

Technikailag csak referenciatípusok lehetnek $null. A PowerShell azonban nagyon nagylelkű, és lehetővé teszi, hogy a változók bármilyen típusúak legyenek. Ha úgy dönt, hogy erősen beír egy értéktípust, az nem lehet $null. A PowerShell számos típus alapértelmezett értékére konvertálható $null .

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

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

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

Vannak olyan típusok, amelyek nem rendelkeznek érvényes konverzióval a következőből $null: . Ezek a típusok hibát okoznak Cannot convert null to type .

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

Függvényparaméterek

A függvényparaméterek erősen beírt értékeinek használata nagyon gyakori. Általában akkor is megtanuljuk definiálni a paraméterek típusait, ha a szkriptekben általában nem definiáljuk más változók típusait. Előfordulhat, hogy már van néhány erősen beírt változó a függvényekben, és nem is veszi észre.

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

Amint beállítja a paraméter stringtípusát, az érték soha nem lehet $null. Gyakran ellenőrzik, hogy egy érték $null-e, hogy megállapítsák, adott-e a felhasználó értéket vagy sem.

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

$Value üres sztring '' , ha nincs megadva érték. Használja inkább az automatikus változót $PSBoundParameters.Value .

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

$PSBoundParameters csak a függvény meghívásakor megadott paramétereket tartalmazza. A tulajdonság ellenőrzéséhez a ContainsKey metódust is használhatja.

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

IsNotNullOrEmpty

Ha az érték egy sztring, statikus sztringfüggvény használatával ellenőrizheti, hogy az $null érték vagy egy üres sztring egyszerre.

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

Gyakran használom ezt, amikor tudom, hogy az értéktípusnak sztringnek kell lennie.

Amikor ellenőrizni $null

Védekező szkriptelő vagyok. Amikor meghívok egy függvényt, és hozzárendelem egy változóhoz, megnézem $null.

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

Én sokkal szívesebben használja if , vagy foreach több mint használ try/catch. Ne érts félre, még mindig sokat használok try/catch . Ha azonban hibaállapotot vagy üres eredményhalmazt tudok tesztelni, engedélyezhetem, hogy a kivételkezelés valódi kivételeket eredményez.

Én is hajlamos vagyok ellenőrizni $null , mielőtt indexelek egy értéket, vagy metódusokat hívok meg egy objektumon. Ez a két művelet nem működik egy $null objektum esetében, ezért fontosnak tartom, hogy először érvényesítsük őket. Már foglalkoztam ezekkel a forgatókönyvekkel korábban ebben a bejegyzésben.

Nincs találati forgatókönyv

Fontos tudni, hogy a különböző függvények és parancsok eltérően kezelik a találatok nélküli forgatókönyvet. Számos PowerShell-parancs a számba veendő null értéket és egy hibát ad vissza a hibastreamben. Mások azonban kivételeket vetnek ki, vagy állapotobjektumot adnak. Továbbra is önön múlik, hogy a használt parancsok hogyan kezelik a találat nélküli és hibás helyzeteket.

Inicializálja $null-ra

Az egyik szokás, amit felvettem, hogy inicializálom az összes változómat, mielőtt használnám őket. Ezt más nyelveken is meg kell tennie. A függvény tetején vagy egy foreach ciklus beírásakor definiálom az összes használt értéket.

Íme egy forgatókönyv, amelyet szeretném, ha megvizsgálna. Ez egy példa arra a hibára, amit előbb le kellett üldöznem.

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

Az elvárás itt az, hogy Get-Something egy eredményt vagy egy számba veendő null értéket ad vissza. Ha hiba történt, naplózzuk. Ezután ellenőrizzük, hogy van-e érvényes eredmény a feldolgozás előtt.

A kódban elrejtett hiba az, amikor Get-Something kivételt jelez, és nem rendel hozzá értéket $result. A hozzárendelés előtt meghiúsul, így még a $null változóhoz sem rendelünk hozzá$result. $result továbbra is tartalmazza a többi iterációból származó előző érvényes $result. Update-Something többször is végrehajthatja ugyanazon az objektumon ebben a példában.

A probléma enyhítésére a $result-t $null-re állítom be közvetlenül a foreach cikluson belül, mielőtt használnám.

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

Hatókörrel kapcsolatos problémák

Ez segít a hatókörkezelési problémák megoldásában is. Ebben a példában egy ciklusban rendelünk hozzá értékeket $result újra és újra. Mivel azonban a PowerShell lehetővé teszi, hogy a függvényen kívüli változó értékek kifutódjanak az aktuális függvény hatókörébe, a függvényen belüli inicializálás csökkenti az így bevezethető hibákat.

Egy függvény nem inicializálódott változója nem $null, ha egy szülő hatókörben lévő értékre van beállítva. A szülőhatókör lehet egy másik függvény, amely meghívja a függvényt, és ugyanazokat a változóneveket használja.

Ha ugyanezt Do-something a példát venném fel, és eltávolítanám a hurkot, akkor a következő példához hasonló eredményt adnék:

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

Ha a Get-Something hívás kivételt dob, akkor az ellenőrzésem megtalálja a $null$resultInvoke-Something-ból. A függvényen belüli érték inicializálása csökkenti ezt a problémát.

A változók elnevezése nehéz, és gyakran előfordul, hogy egy szerző ugyanazt a változónevet használja több függvényben. Tudom, hogy mindig ezt használom$node$result$data. Így nagyon könnyű lenne, ha a különböző hatókörök értékei olyan helyeken jelennének meg, ahol nem kellene.

Kimenet átirányítása $null

Az egész cikkben $null értékekről beszéltem, de a témakör nem teljes, ha nem említeném a kimenet $null átirányítását. Vannak olyan parancsok, amelyek le szeretné tiltani az adatokat vagy objektumokat. A kimenet $null-ra való átirányítása ezt teszi.

Kimenet-Nulla

A Out-Null parancs a folyamatadatok $nullátirányításának beépített módja.

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

Hozzárendelés $nullhoz

A parancsok $null eredményeit ugyanahhoz a hatáshoz rendelheti hozzá, mint a használatával Out-Null.

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

Mivel $null ez egy állandó érték, soha nem írhatja felül. Nem tetszik, ahogy a kódban néz ki, de gyakran gyorsabban teljesít, mint Out-Null.

Átirányítás $null

Az átirányítási operátorral kimenetet is küldhet a rendszernek $null.

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

Ha olyan parancssori végrehajtható fájlokkal foglalkozik, amelyek a különböző streameken futnak. Az összes kimeneti streamet $null a következőképpen irányíthatja át:

git status *> $null

Összefoglalás

Sokat foglalkoztam ezzel, és tudom, hogy ez a cikk töredezettebb, mint a legtöbb mély merülésem. Ennek az az oka, hogy $null az értékek számos különböző helyen előugranak a PowerShellben, és az összes árnyalat a megtalálás helyére jellemző. Remélem, hogy ezzel egy jobb megértéssel távozol $null, és a kevésbé ismert helyzetek tudásának birtokában.