$null hakkında bilmek istediğiniz her şey
PowerShell $null genellikle basit gibi görünür ancak birçok nüansı vardır. Beklenmedik bir şekilde $null değeriyle karşılaştığınızda ne olacağını bilmeniz için $null'a yakından bakalım.
Not
Bu makalenin orijinal versiyonu @KevinMarquette tarafından yazılan blogda yayınlandı. PowerShell ekibi bu içeriği bizimle paylaştığı için Kevin'e teşekkür ediyor. Lütfen PowerShellExplained.com adresindeki bloguna göz atın.
NULL nedir?
NULL'u bilinmeyen veya boş bir değer olarak düşünebilirsiniz. Bir değişken, siz ona bir değer veya nesne atayana kadar NULL'dur. Bu önemli olabilir çünkü değer gerektiren ve değer NULL ise hata üreten bazı komutlar vardır.
PowerShell $null
$null, PowerShell'de NULL'u 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ğerini NULL değerine sahip bir nesne olarak ele alır. Bu, başka bir dilden geliyorsanız beklediğinizden farklıdır.
$null örnekleri
Başlatmadığınız bir değişkeni kullanmaya çalıştığınızda değer $null olur. Bu, $null değerlerinin kodunuza gizlice sızmasının en yaygın yollarından biridir.
PS> $null -eq $undefinedVariable
True
Bir değişken adını yanlış yazarsanız PowerShell bunu farklı bir değişken olarak görür ve değer $null olur.
$null değerlerini bulmanın diğer yolu, bunların size sonuç vermeyen diğer komutlardan gelmesidir.
PS> function Get-Nothing {}
PS> $value = Get-Nothing
PS> $null -eq $value
True
$null'un etkisi
$null değerleri, göründükleri yere bağlı olarak kodunuzu farklı şekilde etkiler.
Dizelerde
Bir dizede $null kullanırsanız, bu boş bir değerdir (veya boş dizedir).
PS> $value = $null
PS> Write-Output "The value is $value"
The value is
Günlük mesajlarında değişkenleri kullanırken parantez içine almayı sevmemin nedenlerinden biri de budur. Değer dizenin sonunda olduğunda 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ğerlerinin fark edilmesini kolaylaştırır.
Sayısal denklemde
Sayısal bir denklemde $null değeri kullanıldığında sonuçlarınız hata vermiyorsa geçersizdir. Bazen $null 0 olarak değerlendirilir ve diğer zamanlarda sonucun tamamı $null olur. Burada, değerlerin sırasına bağlı olarak 0 veya $null veren bir çarpma örneği 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 bir dizin kullanmanıza olanak tanır. Gerçekte null olan bir koleksiyona dizin oluşturmaya çalışırsanız şu hatayı alırsınız: Boş bir diziye dizin oluşturulamıyor.
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 $null sonucu alırsınız.
$array = @( 'one','two','three' )
$null -eq $array[100]
True
Bir nesnenin yerine
Belirtilen özelliğe sahip olmayan bir nesnenin özelliğine veya alt özelliğine erişmeye çalışırsanız, tanımsız bir değişken için alacağınız gibi bir $null değeri elde edersiniz. Bu durumda değişkenin $null veya gerçek bir nesne olması önemli değildir.
PS> $null -eq $undefined.some.fake.property
True
PS> $date = Get-Date
PS> $null -eq $date.some.fake.property
True
Boş değerli bir ifadeye ilişkin yöntem
Bir $null nesnesinde bir yöntemin çağrılması, bir RuntimeException oluş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
Ne zaman Boş değerli bir ifadede yöntem çağıramazsınız ifadesini görsem, ilk olarak $için kontrol etmeden bir değişken üzerinde yöntem çağırdığım yerler ararım. boş.
$null olup olmadığı kontrol ediliyor
Örneklerimde $null'u kontrol ederken her zaman $null'u sol tarafa yerleştirdiğimi fark etmişsinizdir. Bu kasıtlıdır ve PowerShell'in en iyi uygulaması olarak kabul edilir. Sağa yerleştirmenin size beklenen sonucu vermediği bazı senaryolar vardır.
Bir 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'
}
$value'ı tanımlamazsam, ilki $true olarak değerlendirilir ve mesajımız Dizi $null olur. Buradaki tuzak, her ikisinin de $false olmasına izin veren bir $value oluşturmanın mümkün olmasıdır.
$value = @( $null )
Bu durumda $value, $null içeren bir dizidir. -eq dizideki her değeri kontrol eder ve eşleşen $null değerini döndürür. Bu, $false olarak değerlendirilir. -ne, $null ile eşleşmeyen her şeyi döndürür ve bu durumda sonuç yoktur (Bu aynı zamanda $false olarak da değerlendirilir). Her ne kadar biri olması gerektiği gibi görünse de hiçbiri $true değil.
Her ikisinin de $false olarak değerlendirilmesini sağlayan bir değer oluşturmakla kalmayıp, her ikisinin de $true olarak değerlendirilmesini sağlayan bir değer oluşturmak da mümkündür. Mathias Jessen (@IISResetMe) bu senaryoyu ele alan güzel bir gönderiye sahip.
PSScriptAnalyzer ve VSCode
PSScriptAnalyzer modülünde bu sorunu kontrol eden PSPossibleIncorrectComparisonWithNull adında bir kural bulunur.
PS> Invoke-ScriptAnalyzer ./myscript.ps1
RuleName Message
-------- -------
PSPossibleIncorrectComparisonWithNull $null should be on the left side of equality comparisons.
VS Code, PSScriptAnalyser kurallarını da kullandığından, bunu betiğinizdeki bir sorun olarak da vurgular veya tanımlar.
Basit olup olmadığını kontrol edin
İnsanların $null olmayan bir değeri kontrol etmesinin yaygın bir yolu, karşılaştırma olmadan basit bir if() ifadesi kullanmaktır.
if ( $value )
{
Do-Something
}
Değer $null ise bu, $false olarak değerlendirilir. Bunu okumak kolaydır, ancak tam olarak aramasını beklediğiniz şeyi aradığına dikkat edin. O kod satırını şöyle okudum:
$value'nin bir değeri varsa.
Ama hikayenin tamamı bu değil. Bu satır aslında şunu söylüyor:
$value$nullveya0veya$falseveya boş bir dize veya boş bir dizi değilse.
İşte bu ifadenin daha eksiksiz bir örneği.
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
}
Yalnızca bir değişkenin bir değere sahip olduğunu değil, diğer değerlerin de $false olarak sayıldığını hatırladığınız sürece, temel bir if kontrolünü kullanmakta hiçbir sakınca yoktur.
Birkaç gün önce bazı kodları yeniden düzenlerken bu sorunla karşılaştım. Bunun gibi temel bir özellik kontrolü vardı.
if ( $object.property )
{
$object.property = $value
}
Nesne özelliğine yalnızca mevcutsa bir değer atamak istedim. Çoğu durumda orijinal nesne, if ifadesinde $true olarak değerlendirilecek bir değere sahipti. Ancak değerin bazen ayarlanamadığı bir sorunla karşılaştım. Kodun hatalarını ayıkladım ve nesnenin bu özelliğe sahip olduğunu ancak bunun boş bir dize değeri olduğunu buldum. Bu, önceki mantıkla güncellenmesini engelledi. Böylece uygun bir $null kontrolü ekledim ve her şey işe yaradı.
if ( $null -ne $object.property )
{
$object.property = $value
}
Bunlar gibi tespit edilmesi zor olan küçük hatalar, $null değerlerini agresif bir şekilde kontrol etmeme neden oluyor.
$null.Count
Bir $null değerindeki bir özelliğe erişmeye çalışırsanız bu özellik de $null olur. count özelliği bu kuralın istisnasıdır.
PS> $value = $null
PS> $value.count
0
Bir $null değeriniz olduğunda count 0 olur. Bu özel özellik PowerShell tarafından eklenmiştir.
[PSCustomObject] Sayısı
PowerShell'deki hemen hemen tüm nesneler bu count özelliğine sahiptir. Önemli bir istisna, Windows PowerShell 5,1'deki [PSCustomObject]'dir (Bu, PowerShell 6,0'da düzeltilmiştir). Count özelliği bulunmadığından, kullanmaya çalıştığınızda $null değeri elde edersiniz. $null kontrolü yerine .Count kullanmaya çalışmamanız için bunu burada belirtiyorum.
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
$null'ın diğerlerinden farklı davranan özel bir türü vardır. Ben buna numaralandırılabilir null diyeceğim ama bu gerçekten bir System.Management.Automation.Internal.AutomationNull. Bu numaralandırılabilir null, hiçbir şey döndürmeyen bir işlev veya komut dosyası bloğunun sonucu olarak elde ettiğiniz değerdir (geçersiz sonuç).
PS> function Get-Nothing {}
PS> $nothing = Get-Nothing
PS> $null -eq $nothing
True
Bunu $null ile karşılaştırırsanız $null değerini elde edersiniz. Bir değerin gerekli olduğu bir değerlendirmede kullanıldığında değer her zaman $null olur. Ancak onu 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ğeri içeren ve count değeri 1 olan bir diziniz olabilir. Ancak bir dizinin içine boş bir dizi yerleştirirseniz bu bir öğe olarak sayılmaz. Sayı 0.
Numaralandırılabilir null'a bir koleksiyon gibi davranırsanız, o zaman boştur.
Kesin olarak yazılmayan bir işlev parametresine numaralandırılabilir bir boş değer iletirseniz, PowerShell numaralandırılabilir boş değeri varsayılan olarak bir $null değerine zorlar. Bu, işlevin içinde değerin System.Management.Automation.Internal.AutomationNull türü yerine $null olarak değerlendirildiği anlamına gelir.
Boru hattı
Farkı gördüğünüz ilk yer boru hattını kullandığınız zamandır. Bir $null değeri aktarabilirsiniz ancak numaralandırılabilir bir boş değer veremezsiniz.
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'u hesaba katmalısınız.
Önce $null olup olmadığını kontrol edin
- İşlem hattındaki boş değeri filtreleyin (
... | Where {$null -ne $_} | ...) - Boru hattı işlevinde bunu kullanın
her biri
foreach'un en sevdiğim özelliklerinden biri de $null koleksiyonu üzerinde numaralandırılmamasıdır.
foreach ( $node in $null )
{
#skipped
}
Bu beni koleksiyonu numaralandırmadan önce $null kontrol etme zorunluluğundan kurtarıyor. $null değerlerden oluşan bir koleksiyonunuz varsa, $node yine de $null olabilir.
Foreach, PowerShell 3.0 ile bu şekilde çalışmaya başladı. Daha eski bir sürümdeyseniz durum böyle değildir. Bu, 2.0 uyumluluğu için kodu geriye taşırken 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ına izin verir. Bir değer türünü kesin olarak yazmaya karar verirseniz bu $null olamaz. PowerShell, $null değerini 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'dan geçerli bir dönüşümü olmayan bazı türler vardır. Bu türler Null'u türe dönüştüremiyor hatası 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
Fonksiyon parametreleri
İşlev parametrelerinde kesin olarak yazılan değerlerin kullanılması çok yaygındır. Komut dosyalarımızda diğer değişkenlerin türlerini tanımlamama eğiliminde olsak da genellikle parametrelerimizin türlerini tanımlamayı öğreniriz. İşlevlerinizde zaten kesin olarak yazılmış bazı değişkenler olabilir ve bunun farkında bile olmayabilirsiniz.
function Do-Something
{
param(
[String] $Value
)
}
Parametrenin türünü string olarak ayarladığınızda değer hiçbir zaman $null olamaz. Kullanıcının bir değer sağlayıp sağlamadığını görmek için bir değerin $null olup olmadığını kontrol etmek yaygın bir uygulamadır.
if ( $null -ne $Value ){...}
$Value, hiçbir değer sağlanmadığında boş bir '' dizesidir. Bunun yerine $PSBoundParameters.Value otomatik değişkenini 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 $null mu yoksa boş bir dize mi olduğunu kontrol etmek 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ıklıkla kullanırken buluyorum.
$null kontrolü yaptığımda
Ben savunma senaryo yazarıyım. Ne zaman bir işlevi çağırsam ve onu bir değişkene atasam, onu $null açısından kontrol ederim.
$userList = Get-ADUser kevmar
if ($null -ne $userList){...}
try/catch yerine if veya foreach kullanmayı tercih ediyorum. Beni yanlış anlamayın, hala try/catch'i çok kullanıyorum. Ancak bir hata durumunu veya boş bir sonuç kümesini test edebilirsem, istisna işlememin gerçek istisnalar için olmasına izin verebilirim.
Ayrıca bir değere indekslemeden veya bir nesnedeki yöntemleri çağırmadan önce $null olup olmadığını kontrol etme eğilimindeyim. Bu iki eylem bir $null nesnesi için başarısız olduğundan, önce bunları doğrulamanın önemli olduğunu düşünüyorum. Bu senaryoları bu yazının başlarında ele almıştım.
Sonuç senaryosu yok
Farklı işlevlerin ve komutların sonuçsuz senaryoyu farklı şekilde ele aldığını bilmek önemlidir. Birçok PowerShell komutu, numaralandırılabilir boş değeri ve hata akışında bir hatayı döndürür. Ancak diğerleri istisnalar atar veya size bir durum nesnesi verir. Kullandığınız komutların sonuçsuz kalma ve hata senaryolarıyla nasıl başa çıktığını bilmek hala size kalmış.
$null olarak başlatılıyor
Edindiğim bir alışkanlık, tüm değişkenlerimi kullanmadan önce başlatmaktır. Bunu diğer dillerde yapmanız gerekmektedir. Fonksiyonumun en üstünde veya foreach döngüsüne girerken kullandığım tüm değerleri tanımlarım.
İşte yakından bakmanızı istediğim bir senaryo. Daha önce kovalamak zorunda kaldığım bir hatanın ö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
}
}
}
Buradaki beklenti, Get-Something'in ya bir sonuç ya da numaralandırılabilir bir boş değer döndürmesidir. Eğer bir hata varsa onu günlüğe kaydediyoruz. Daha sonra işleme koymadan önce geçerli bir sonuç aldığımızdan emin olmak için kontrol ederiz.
Bu kodda gizlenen hata, Get-Something'in bir istisna atması ve $result'a bir değer atamamasıdır. Atama öncesinde başarısız olur, dolayısıyla $result değişkenine $null değerini bile atamayız. $result hâlâ diğer yinelemelerdeki önceki geçerli $result'u içeriyor. Bu örnekte aynı nesne üzerinde birden çok kez yürütmek için Update-Something.
Bu sorunu hafifletmek için kullanmadan önce $result'u foreach döngüsünün hemen içinde $null olarak ayarladım.
foreach ( $node in 1..6 )
{
$result = $null
try
{
...
Kapsam sorunları
Bu aynı zamanda kapsam belirleme sorunlarının azaltılmasına da yardımcı olur. Bu örnekte $result'a bir döngü içinde tekrar tekrar değerler atadık. Ancak PowerShell, işlevin dışından gelen değişken değerlerin geçerli işlevin kapsamına girmesine izin verdiğinden, bunları işlevinizin içinde başlatmak, bu şekilde ortaya çıkabilecek hataları azaltır.
İşlevinizdeki başlatılmamış bir değişken, üst kapsamdaki bir değere ayarlanmışsa $null değildir. Ana kapsam, işlevinizi çağıran ve aynı değişken adlarını kullanan başka bir işlev olabilir.
Aynı Bir şey yap örneğini alıp döngüyü kaldırırsam, şu ö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 istisna atarsa, $null kontrolüm Invoke-Something'den $result'u bulur. kodu >. İşlevinizin içindeki değerin başlatılması bu sorunu azaltır.
Değişkenleri adlandırmak zordur ve bir yazarın aynı değişken adlarını birden fazla işlevde kullanması yaygındır. Her zaman $node,$result,$data kullandığımı biliyorum. Dolayısıyla farklı kapsamlardaki değerlerin olmaması gereken yerlerde ortaya çıkması çok kolay olacaktır.
Çıktıyı $null'a yönlendir
Bu makalenin tamamı boyunca $null değerlerinden bahsettim ancak çıktının $null'a yönlendirildiğinden bahsetmediysem konu tamamlanmış sayılmaz. Bastırmak istediğiniz bilgi veya nesnelerin çıktısını veren komutlarınızın olduğu zamanlar vardır. Çıktıyı $null'a yönlendirmek bunu yapar.
Out-Null
Out-Null komutu, işlem hattı verilerini $null'a yönlendirmenin yerleşik yoludur.
New-Item -Type Directory -Path $path | Out-Null
$null'a ata
Out-Null kullanımıyla aynı etkiyi elde etmek için bir komutun sonuçlarını $null öğesine atayabilirsiniz.
$null = New-Item -Type Directory -Path $path
$null sabit bir değer olduğundan hiçbir zaman üzerine yazamazsınız. Kodumdaki görünüşünü beğenmiyorum ama genellikle Out-Null'dan daha hızlı performans gösteriyor.
$null'a yönlendir
Çıktıyı $null'a göndermek için yeniden yönlendirme operatörünü de kullanabilirsiniz.
New-Item -Type Directory -Path $path > $null
Farklı akışlarda çıktı veren komut satırı yürütülebilir dosyalarıyla ilgileniyorsanız. Tüm çıktı akışlarını şu şekilde $null'a yönlendirebilirsiniz:
git status *> $null
Özet
Bu konuda çok fazla yol kat ettim ve bu makalenin, derinlemesine incelemelerimin çoğundan daha parçalı olduğunu biliyorum. Bunun nedeni, $null değerlerinin PowerShell'de birçok farklı yerde ortaya çıkabilmesi ve tüm nüansların onu bulduğunuz yere özel olmasıdır. Umarım $null'u daha iyi anlayarak ve karşılaşabileceğiniz daha belirsiz senaryoların farkındalığıyla bu durumdan uzaklaşırsınız.