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.

Feljegyzés

A cikk eredeti verziója @KevinMarquette által írt blogon jelent meg. A PowerShell csapata köszönjük Kevinnek, hogy megosztotta velünk ezt a tartalmat. Kérjük, nézze meg a blogját a PowerShellExplained.com.

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

Ha egy $null numerikus egyenletben értéket használ, az eredmények érvénytelenek lesznek, ha nem adnak 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 metódus meghívása egy $null objektumon 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 én mindig a $null bal oldalon, amikor ellenőrzi $null az én példákat. 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ő kiértékeli , $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 a kiértékelés eredménye.$false A -ne visszaad mindent, ami nem egyezik, $null és ebben az esetben nincsenek eredmények (Ez is kiértékeli).$false Egyik $true sem annak ellenére, hogy úgy néz ki, hogy az egyiknek kell lennie.

Nem csak olyan értéket hozhatunk létre, amely mindkettőt kiértékeli $false, hanem olyan értéket is létrehozhatunk, amelyben mindketten kiértékelik őket $true. Mathias Jessen (@IISResetMe) van egy jó post , hogy merüljön el, hogy a forgatókönyv.

PSScriptAnalyzer és VSCode

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 vá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, ha emlékszik a többi értékre $false , és nem csak arra, 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 észleltem a kódon, és megállapítottam, hogy az objektum rendelkezik a tulajdonságmal, de üres sztringérték 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. Számít

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 count .0 Ezt a speciális tulajdonságot a PowerShell hozzáadja.

[PSCustomObject] Számít

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). Nem rendelkezik darabszámtulajdonságsal, ezért ha használni próbálja, kap egy $null értéket. Azért hívom ezt ide, hogy ne próbálja meg csekk helyett $null használni.Count.

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

Üres null

Van egy speciális típus $null , amely másképp viselkedik, mint a többi. Üresnek fogom nevezni$null, de valójában System.Management.Automation.Internal.AutomationNull. Ez az üres $null az, amelyet egy függvény vagy szkriptblokk eredményeként kap, amely semmit (üres eredményt) ad vissza.

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

Ha összehasonlítja a értékkel $null, egy értéket kap $null . 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

Egy olyan tömb is lehet, amely egy $null értéket tartalmaz, és a tömb értéke count .1 Ha azonban üres eredményt helyez el egy tömbben, akkor az nem számít elemnek. A szám .0

Ha gyűjteményként kezeli az üreset $null , akkor üres.

Ha üres értéket ad át egy nem erősen beírt függvényparaméternek, a PowerShell alapértelmezés szerint a semmi értékét egy $null értékre kényszeríti. Ez azt jelenti, hogy a függvényen belül az érték a System.Management.Automation.Internal.AutomationNull típus helyett lesz kezelve.$null

Folyamat

A különbség elsődleges helye a folyamat használata. Az értékeket csövezheti $null , üres $null értéket azonban nem.

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

A kódtól függően figyelembe kell vennie a $null logikát.

Vagy ellenőrizze az elsőt $null

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

foreach

Az egyik kedvenc funkciója foreach az, hogy nem számba veszi a $null gyűjtemény.

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

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

A foreach így kezdett dolgozni a PowerShell 3.0-val. Ha történetesen egy régebbi verzión dolgozik, akkor ez nem így van. 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 előfordul, hogy ellenőrzi, hogy a $null felhasználó adott-e é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.

Azt is hajlamosak 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 az üres $null és a hibastreamben lévő hibát adja vissza. Mások azonban kivételeket vetnek ki, vagy állapotobjektumot adnak. Továbbra is önön múlik, hogy a használt parancsok hogyan kezelik az eredményeket és a hibaforgatókönyveket.

Inicializálás a $null

Az egyik szokás, amit felvettem, hogy inicializálja az összes változót, 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ása közben definiálom az összes értéket, amelyet használok.

Í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 üres $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 $result változóhoz sem rendelünk hozzá$null. $result a többi iterációból származó előző érvényeset $result továbbra is tartalmazza. Update-Something többször is végrehajthatja ugyanazon az objektumon ebben a példában.

A foreach hurokon belülre állítottam $result$null , mielőtt használnám a probléma megoldásához.

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.

A függvény nem inicializált változója nem $null akkor, 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 hívás kivételt jelez Get-Something , akkor a csekk megkeresi $null a $result következőt Invoke-Something: . 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 cikk értékeiről $null beszéltem, de a témakör nem teljes, ha nem említettem a kimenet $nullátirányítását. Vannak olyan parancsok, amelyek le szeretné tiltani az adatokat vagy objektumokat. A kimenet átirányítása erre $null .

Out-Null

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 $null

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

Összegzé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 elsétálsz ettől egy jobb megértéssel $null és a homályosabb forgatókönyvek tudatosításával.