Aracılığıyla paylaş


$null hakkında bilmek istediğiniz her şey

PowerShell $null genellikle basit gibi görünür, ancak çok fazla nüansları vardır. Haydi $null'a yakından bakalım, böylece beklenmedik bir şekilde bir $null değeriyle karşılaştığınızda ne olacağını bilirsiniz.

Uyarı

Bu makalenin özgün sürümü, @KevinMarquette tarafından yazılan blogda yer almıştır. PowerShell ekibi, bu içeriği bizimle paylaştığı için Kevin'e teşekkür ederiz. Lütfen PowerShellExplained.com'daki blogunu inceleyin.

NULL nedir?

NULL değerini bilinmeyen veya boş bir değer olarak düşünebilirsiniz. Bir değer veya nesne atayana kadar değişken NULL olur. Değer gerektiren ve değer NULL olduğunda hata oluşturan bazı komutlar olduğundan bu önemli olabilir.

PowerShell $null

$null , PowerShell'de NULL değerini temsil etmek için kullanılan otomatik bir değişkendir. Bunu değişkenlere atayabilir, karşılaştırmalarda kullanabilir ve bir koleksiyonda NULL için yer tutucu olarak kullanabilirsiniz.

PowerShell, NULL değerine sahip bir nesne olarak kabul eder $null . Bu, başka bir dilden geliyorsanız bekleyebileceğinizden farklıdır.

$null örnekleri

Başlatmadığınız bir değişken kullanmayı denediğinizde, değeri şeklindedir $null. Bu, değerlerin kodunuz içine sızmasının $null en yaygın yollarından biridir.

PS> $null -eq $undefinedVariable
True

Bir değişken adını yanlış yazdığınızda PowerShell bunu farklı bir değişken olarak görür ve değeri olur $null.

$null değerlerini bulmanın başka bir yolu, size hiçbir sonuç vermeyen diğer komutlardan gelmeleridir.

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

$null'ın etkisi

$null değerleri, kodunuzun nerede göründüğüne bağlı olarak farklı şekilde etkiler.

Dizelerde

Bir dizede kullanıyorsanız $null , bu boş bir değerdir (veya boş dizedir).

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

Bu, günlük iletilerinde kullanırken değişkenlerin çevresine köşeli ayraç yerleştirmeyi sevmemin nedenlerinden biridir. Değer dizenin sonundayken değişken değerlerinizin kenarlarını tanımlamak daha da önemlidir.

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

Bu, boş dizelerin ve $null değerlerin kolayca tespit olmasını sağlar.

Sayısal bir denklemde

Sayısal denklemde bir $null değer kullanıldığında, hata vermezlerse sonuçlarınız geçersiz olur. Bazen $null, 0 olarak değerlendirilir ve diğer zamanlarda tüm sonucu $null yapar. Değerlerin sırasına bağlı olarak 0 veya $null üreten bir çarpım örneği burada verilmiştir.

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

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

Koleksiyon yerine

Koleksiyon, değerlere erişmek için dizin kullanmanıza olanak tanır. Aslında nullolan bir koleksiyonda dizin oluşturmaya çalışırsanız şu hatayı alırsınız: 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

Bir koleksiyonunuz varsa ancak koleksiyonda olmayan bir öğeye erişmeye çalışırsanız, bir $null sonuç alırsınız.

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

Nesne yerine

Belirtilen özelliğe sahip olmayan bir nesnenin özelliğine veya alt özelliğine erişmeye çalışırsanız, tanımlanmamış bir $null değişken için yaptığınız gibi bir değer alırsınız. Değişkenin $null bu durumda gerçek bir nesne olup olmadığı önemli değildir.

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

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

Null değere sahip bir ifadenin yöntemi

Bir $null nesnede bir yöntemi çağırmak bir RuntimeExceptionoluşturur.

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

ifadeyi You cannot call a method on a null-valued expression her gördüğümde, ilk baktığım şey, bir değişkende $null kontrol etmeden bir fonksiyonu çağırdığım yerlerdir.

$null kontrolü

Örneklerimi kontrol $null ederken her zaman sol tarafa yerleştirdiğim $null fark etmiş olabilirsiniz. Bu, kasıtlı olarak yapılır ve PowerShell en iyi uygulaması olarak kabul edilir. Sağa yerleştirmenin size beklenen sonucu vermediği bazı senaryolar vardır.

Sonraki örneğe bakın ve sonuçları tahmin etmeye çalışın:

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

Eğer $value tanımlamazsam, ilk olan $true ile değerlendirilir ve iletimiz The array is $null olur. Buradaki tuzak, her ikisinin de $value olmasına izin veren bir $false yaratmanın mümkün olmasıdır.

$value = @( $null )

Bu durumda, $value bir $null içeren dizidir. dizideki -eq her değeri denetler ve eşleşen değeri $null döndürür. Bu, $false olarak değerlendirilir. -ne eşleşmeyen $null her şeyi döndürür ve bu durumda sonuç yoktur (Bu ayrıca $false olarak değerlendirilir). İkisi de $true değil, oysa biri öyle olmalı gibi görünüyor.

tr-TR: Hem ikisinin de $false olarak değerlendirilmesini sağlayacak bir değer oluşturmak mümkündür, hem de her ikisinin $true olarak değerlendirdiği bir değer oluşturmak mümkündür. Mathias Jessen (@IISResetMe) bu senaryoya göz atan iyi bir gönderiye sahiptir.

PSScriptAnalyzer ve VS Code

PSScriptAnalyzer modülü, adlı PSPossibleIncorrectComparisonWithNullbu sorunu denetleen bir kurala sahiptir.

PS> Invoke-ScriptAnalyzer ./myscript.ps1

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

VS Code da PSScriptAnalyser kurallarını kullandığından, bunu betiğinizde bir sorun olarak vurgular veya tanımlar.

Basit if kontrolü

kişilerin $null olmayan bir değeri denetlemesinin yaygın bir yolu, karşılaştırma olmadan basit if() bir deyim kullanmaktır.

if ( $value )
{
    Do-Something
}

Değer $null ise, bu $false olarak değerlendirilir. Bunu okumak kolaydır, ancak tam olarak beklediğiniz şeyi aradığından emin olun. Bu kod satırını şöyle okudum:

$value bir değeri varsa.

Ama bütün hikaye bu değil. Bu satır aslında şunu söylüyor:

Eğer $value veya $null veya 0 veya $false veya boş bir dize ya da boş bir dizi değilse.

Bu deyimin daha eksiksiz bir örneği aşağıda verilmiştir.

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
}

Temel bir if kontrolü kullanmak, diğer değerlerin $false olarak sayıldığını ve yalnızca bir değişkenin değeriyle sınırlı olmadığını hatırladığınız sürece tamamen kabul edilebilirdir.

Birkaç gün önce bazı kodları yeniden düzenlediğimde bu sorunla karşılaştım. Bunun gibi temel bir özellik denetimi vardı.

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

Nesne özelliğine yalnızca varsa bir değer atamak istedim. Çoğu durumda, özgün nesnenin, $true ifadesinde if olarak değerlendirilecek bir değeri vardı. Ancak bazen değerin ayarlanmamasıyla ilgili bir sorunla karşılaştım. Kodda hata ayıklaması yaptım ve nesnenin özelliğine sahip olduğunu ancak boş bir dize değeri olduğunu tespit ettim. Bu, önceki mantıkla güncelleştirilmesini engelledi. Bu yüzden uygun bir $null kontrol ekledim ve her şey işe yaradı.

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

Bunun gibi küçük hataları tespit etmek zor ve için değerleri $nullagresif bir şekilde kontrol etmemi sağlar.

$null. Saymak

Bir $null değerinin bir özelliğine erişmeye çalışırsanız, bu özelliğin de $null olduğunu görürsünüz. Count özelliği bu kuralın özel durumudur.

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

Bir $null değeriniz olduğunda, Count0 olur. Bu özel özellik PowerShell tarafından eklenir.

[PSCustomObject] Sayı

PowerShell'deki neredeyse tüm nesneler bu Count özelliğe sahiptir. Windows PowerShell 5.1'deki önemli bir özel durumdur [pscustomobject] (Bu, PowerShell 6.0'da düzeltilir). Bir Count özelliği olmadığından, onu kullanmaya çalıştığınızda bir $null değeri elde edersiniz. Burada belirtiyorum ki, Count yerine $null kontrolü kullanmaya çalışmayın.

Bu örneği Windows PowerShell 5.1 ve PowerShell 6.0'da çalıştırmak size farklı sonuçlar verir.

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

Numaralandırılabilir null

Diğerlerinden farklı davranan özel bir tür $null vardır. Numaralandırılabilir null olarak adlandıracağım ama bu gerçekten bir System.Management.Automation.Internal.AutomationNull. Bu numaralandırılabilir null, hiçbir şey döndüremeyen bir işlev veya betik bloğunun sonucu olarak elde ettiğiniz değerdir (geçersiz bir sonuç).

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

ile $nullkarşılaştırırsanız bir $null değer alırsınız. Bir değerin gerekli olduğu bir değerlendirmede kullanıldığında, değer her zaman $nullşeklindedir. Ancak bir dizinin içine yerleştirirseniz, boş bir diziyle aynı şekilde değerlendirilir.

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

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

Bir $null değer içeren ve değeri Count1olan bir diziniz olabilir. Ancak dizi içine boş bir dizi yerleştirirseniz, öğe olarak sayılmaz. Sayı şeklindedir 0.

Numaralandırılabilir null değerini bir koleksiyon gibi ele alırsanız boştur.

Kesin olarak yazılmamış bir işlevin parametresine numaralandırılabilir null değerini geçirirseniz, PowerShell varsayılan olarak bu numaralandırılabilir null değerini bir $null değere dönüştürür. Bu, işlevin içinde değerin $null türü yerine olarak ele alındığı anlamına gelir.

Boruhattı

Farkı en çok gördüğünüz yer, işlem hattını kullanırken olur. $null değerini yönlendirebilirsiniz, ancak numaralandırılabilir null değeri yönlendiremezsiniz.

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

Kodunuza bağlı olarak, mantığınızda $null öğesini hesaba katmanız gerekir.

Ya önce $null kontrol ediniz

  • İşlem hattında null değerini filtreleme (... | where {$null -ne $_} | ...)
  • İşlem hattı fonksiyonunda onu hallet

foreach döngüsü

En sevdiğim özelliklerden foreach biri, bir $null koleksiyon üzerinde numaralandırma yapılmamasıdır.

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

Bu, numaralandırmadan önce koleksiyonu denetlemem gerekme $null neden olur. Bir dizi $null değeriniz varsa, $node yine $null olabilir.

PowerShell foreach 3.0 ile bu şekilde çalışmaya başladı. Daha eski bir sürüm kullanıyorsanız bu durum geçerli değildir. Bu, 2.0 uyumluluğu için kodu geri taşıma sırasında dikkat edilmesi gereken önemli değişikliklerden biridir.

Değer türleri

Teknik olarak, yalnızca referans türleri $null olabilir. Ancak PowerShell çok cömerttir ve değişkenlerin herhangi bir türde olmasını sağlar. Bir değer türünü katı şekilde belirtmeye karar verirseniz, $null olamaz. PowerShell, $null birçok tür için varsayılan değere dönüştürür.

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

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

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

$null'den geçerli bir dönüştürme yolu olmayan bazı türler vardır. Bu türler bir Cannot convert null to type hata oluşturur.

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

İşlev parametreleri

İşlev parametrelerinde kesin olarak belirlenmiş değerler kullanmak çok yaygındır. Genellikle betiklerimizdeki diğer değişken türlerini tanımlamama eğiliminde olsak bile parametrelerimizin türlerini tanımlamayı öğreniriz. İşlevlerinizde zaten kesin olarak belirlenmiş bazı değişkenleriniz olabilir ve bunun farkında bile olmayabilirsiniz.

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

Parametresinin türünü olarak stringayarladığınız anda değer hiçbir zaman olamaz $null. Bir değerin, kullanıcının bir değer sağıp sağlamadığını görmek için olup olmadığını denetlemek yaygın bir durumdur $null .

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

$Value hiçbir değer sağlanmayan boş bir dizedir '' . Bunun yerine otomatik değişkeni $PSBoundParameters.Value kullanın.

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

$PSBoundParameters yalnızca işlev çağrıldığında belirtilen parametreleri içerir. Özelliği kontrol etmek için ContainsKey yöntemini de kullanabilirsiniz.

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

IsNotNullOrEmpty

Değer bir dizeyse, değerin aynı anda boş bir dize olup olmadığını $null denetlemek için statik dize işlevini kullanabilirsiniz.

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

Değer türünün bir dize olması gerektiğini bildiğimde kendimi bunu sık sık kullanıyorum.

Ne zaman kontrol $null

Savunma senaryosunu ben yapıyorum. Bir işlevi çağırıp bir değişkene atadığım her zaman için $nulldenetlenir.

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

if veya foreach kullanmayı try/catch kullanmaya kıyasla tercih ederim. Yanlış anlamadım, hala çok kullanıyorum try/catch . Ancak bir hata koşulu veya boş bir sonuç kümesi için test edebilirsem, özel durum işlememin gerçek özel durumlar için olmasını sağlayabilirim.

Bir nesnedeki bir değerin içine dizin oluşturmadan veya bir nesne üzerinde yöntem çağırmadan önce $null kontrol etme eğilimindeyim. Bu iki eylem bir $null nesne için başarısız olduğundan önce bunları doğrulamayı önemli buluyorum. Bu senaryoları bu gönderinin başlarında zaten ele aldım.

Sonuç senaryosu yok

Farklı işlevlerin ve komutların sonuç yok senaryolarını farklı işlediğini bilmeniz önemlidir. Birçok PowerShell komutu numaralandırılabilir null değerini ve hata akışında bir hata döndürür. Ya da diğerleri istisna oluşturur ya da size bir durum nesnesi verir. Kullandığınız komutların sonuç yok ve hata senaryolarıyla nasıl başa çıkdığını öğrenmek yine size kalmış.

$null başlat

Edindiğim bir alışkanlık, tüm değişkenlerimi kullanmadan önce başlatmak oldu. Bunu başka dillerde yapmanız gerekir. İşlevimin en üstünde veya bir foreach döngüye girerken kullandığım tüm değerleri tanımlarım.

İşte size yakından bakmanızı istediğim bir senaryo. Daha önce kovalamak zorunda kaldığım bir hata örneği.

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

Beklenti, Get-Something'nin bir sonuç veya numaralandırılabilir bir null döndürmesidir. Hata varsa, kaydediyoruz. Ardından işlemeden önce geçerli bir sonuç aldığımızdan emin olmak için kontrol ederiz.

Bu kodda gizlenen hata, Get-Something bir hata fırlatıp $result'e değer atayamaması durumudur. Göreve başlamadan önce başarısız olur, bu nedenle $null, $result değişkenine bile atanmaz. $result yine de diğer yinelemelerden önceki geçerli $result değerleri içerir. Update-Something bu örnekte aynı nesne üzerinde birden çok kez yürütmek için.

Sorunu azaltmak için $result'i $null olarak ayarlıyorum ve bunu kullanmadan önce foreach döngüsünün içine yerleştiriyorum.

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

Kapsam sorunları

Bu, kapsam belirleme sorunlarının azaltılmasına da yardımcı olur. Bu örnekte, döngüde $result'ye tekrar tekrar değer atanır. Ancak PowerShell işlevin dışındaki değişken değerlerin geçerli işlevin kapsamına taşmasına izin verdiğinden, bunları işlevinizin içinde başlatmak, bu şekilde ortaya konabilecek hataları azaltır.

İşlevinizdeki başlatılmamış bir değişken, üst kapsamdaki bir değere ayarlanmışsa değildir $null . Üst kapsam, işlevinizi çağıran ve aynı değişken adlarını kullanan başka bir işlev olabilir.

Aynı Do-something örneği alıp döngüyü kaldırırsam, bu örneğe benzeyen bir şey elde ederim:

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

Get-Something çağrısı bir özel durum oluşturursa, $null denetimim $result içinden Invoke-Something öğesini bulur. İşlevinizin içindeki değerin başlatılması bu sorunu azaltır.

Değişkenleri adlandırmak zordur ve bir yazarın birden çok işlevde aynı değişken adlarını kullanması yaygındır. Her zaman kullandığımı $node$result$data biliyorum. Bu nedenle, farklı kapsamlardaki değerlerin olmaması gereken yerlerde gösterilmesi çok kolay olacaktır.

Çıktıyı $null'a yönlendir.

Bu makalenin tamamı için değerlerden bahsediyorum $null ancak çıktıyı 'a $nullyeniden yönlendirmeden bahsetmediysem konu tamamlanmamıştır. Bazı durumlarda, gizlemesini istediğiniz bilgilerin veya nesnelerin çıkışını veren komutlarınız vardır. Çıktıyı adresine $null yönlendirmek bunu yapar.

Out-Null

Out-Null komutu, işlem hattı verilerini $nullöğesine yeniden yönlendirmenin yerleşik yoludur.

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

$null'a atama yap

Komutun sonuçlarını $null'a atayarak Out-Null kullanmanın aynı etkisini elde edebilirsiniz.

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

$null Sabit bir değer olduğundan, hiçbir zaman üzerine yazasınız. Kodumda görünme şeklini beğenmiyorum ama genellikle Out-Null'den daha hızlı çalışır.

$null'a yeniden yönlendirme

Çıktıyı $null'ye göndermek için yeniden yönlendirme işlecini de kullanabilirsiniz.

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

Farklı akışlarda çıktı üreten komut satırı programlarıyla ilgileniyorsanız. Tüm çıkış akışlarını $null şu şekilde yeniden yönlendirebilirsiniz:

git status *> $null

Özet

Bu konuda kapsamlı bir inceleme yaptım ve bu makalenin detaylı incelemelerimden daha parçalı olduğunu biliyorum. Bunun nedeni $null , değerlerin PowerShell'de birçok farklı yerde açılabilir olması ve tüm nüansların onu bulduğunuz yere özel olmasıdır. Umarım karşılaşabileceğiniz daha belirsiz senaryoları daha iyi anlayıp $null farkındalığınızla bu durumdan uzaklaşırsınız.