Semua yang ingin Anda ketahui tentang $null

PowerShell $null sering tampak sederhana tetapi memiliki banyak nuansa. Mari kita lihat $null lebih dekat sehingga Anda tahu apa yang terjadi ketika Anda secara tak terduga mengalami $null nilai.

Catatan

Versi asli artikel ini muncul di blog yang ditulis oleh @KevinMarquette. Tim PowerShell berterima kasih kepada Kevin karena telah membagikan konten ini kepada kami. Silakan lihat blognya di PowerShellExplained.com.

Apa itu NULL?

Anda dapat menganggap NULL sebagai nilai yang tidak diketahui atau kosong. Variabel adalah NULL hingga Anda menetapkan nilai atau objek ke dalamnya. Ini bisa penting karena ada beberapa perintah yang memerlukan nilai dan menghasilkan kesalahan jika nilainya NULL.

$null PowerShell

$null adalah variabel otomatis dalam PowerShell yang digunakan untuk mewakili NULL. Anda dapat menetapkannya ke variabel, menggunakannya sebagai perbandingan dan menggunakannya sebagai placehold untuk NULL dalam koleksi.

PowerShell memperlakukan $null sebagai objek dengan nilai NULL. Ini berbeda dari yang mungkin Anda harapkan jika Anda berasal dari bahasa lain.

Contoh $null

Setiap kali Anda mencoba menggunakan variabel yang belum Anda inisialisasi, nilainya adalah $null. Ini adalah salah satu cara paling umum yang $null nilainya masuk ke dalam kode Anda.

PS> $null -eq $undefinedVariable
True

Jika Anda salah mengetik nama variabel, Maka PowerShell melihatnya sebagai variabel yang berbeda dan nilainya adalah $null.

Cara lain Anda menemukan $null nilai adalah ketika nilai berasal dari perintah lain yang tidak memberi Anda hasil apa pun.

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

Dampak $null

$null nilai berdampak pada kode Anda secara berbeda tergantung di mana kode tersebut muncul.

Dalam string

Jika Anda menggunakan $null dalam string, maka itu adalah nilai kosong (atau string kosong).

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

Ini adalah salah satu alasan saya suka menempatkan tanda kurung di sekitar variabel saat menggunakannya dalam pesan log. Lebih penting lagi untuk mengidentifikasi tepi nilai variabel Anda ketika nilai berada di akhir string.

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

Ini membuat string dan $null nilai kosong mudah dilihat.

Dalam persamaan numerik

$null Ketika nilai digunakan dalam persamaan numerik, hasil Anda tidak valid jika tidak memberikan kesalahan. $null Terkadang mengevaluasi ke 0 dan di lain waktu membuat seluruh hasil .$null Berikut adalah contoh dengan perkalian yang memberikan 0 atau $null tergantung pada urutan nilai.

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

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

Menggantikan koleksi

Koleksi memungkinkan Anda menggunakan indeks untuk mengakses nilai. Jika Anda mencoba mengindeks ke dalam koleksi yang sebenarnya null, Anda mendapatkan kesalahan ini: 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

Jika Anda memiliki koleksi tetapi mencoba mengakses elemen yang tidak ada dalam koleksi, Anda mendapatkan $null hasilnya.

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

Menggantikan objek

Jika Anda mencoba mengakses properti atau sub properti objek yang tidak memiliki properti yang ditentukan, Anda mendapatkan $null nilai seperti yang Anda lakukan untuk variabel yang tidak ditentukan. Tidak masalah apakah variabel adalah $null atau objek aktual dalam kasus ini.

PS> $null -eq $undefined.some.fake.property
True

PS> $date = Get-Date
PS> $null -eq $date.some.fake.property
True

Metode pada ekspresi bernilai null

Memanggil metode pada $null objek melempar 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

Setiap kali saya melihat frasa You cannot call a method on a null-valued expression maka hal pertama yang saya cari adalah tempat-tempat di mana saya memanggil metode pada variabel tanpa terlebih dahulu memeriksanya untuk $null.

Memeriksa $null

Anda mungkin telah memperhatikan bahwa saya selalu menempatkan $null di sebelah kiri ketika memeriksa $null dalam contoh saya. Ini disengaja dan diterima sebagai praktik terbaik PowerShell. Ada beberapa skenario di mana menempatkannya di sebelah kanan tidak memberi Anda hasil yang diharapkan.

Lihat contoh berikutnya ini dan coba prediksi hasilnya:

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

Jika saya tidak mendefinisikan $value, yang pertama dievaluasi dan $true pesan kami adalah The array is $null. Perangkap di sini adalah bahwa dimungkinkan untuk membuat $value yang memungkinkan keduanya untuk menjadi $false

$value = @( $null )

Dalam hal ini, $value adalah array yang berisi $null. memeriksa -eq setiap nilai dalam array dan mengembalikan $null yang cocok. Ini mengevaluasi ke $false. mengembalikan -ne semua yang tidak cocok $null dan dalam hal ini tidak ada hasil (Ini juga mengevaluasi ke $false). Tidak satu $true pun meskipun terlihat seperti salah satu dari mereka seharusnya.

Kita tidak hanya dapat membuat nilai yang membuat keduanya dievaluasi ke $false, dimungkinkan untuk membuat nilai di mana keduanya mengevaluasi ke $true. Mathias Jessen (@IISResetMe) memiliki postingan bagus yang menyelami skenario itu.

PSScriptAnalyzer dan VSCode

Modul PSScriptAnalyzer memiliki aturan yang memeriksa masalah ini yang disebut PSPossibleIncorrectComparisonWithNull.

PS> Invoke-ScriptAnalyzer ./myscript.ps1

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

Karena Visual Studio Code juga menggunakan aturan PSScriptAnalyser, visual ini juga menyoroti atau mengidentifikasi ini sebagai masalah dalam skrip Anda.

Sederhana jika diperiksa

Cara umum orang memeriksa nilai non-$null adalah dengan menggunakan pernyataan sederhana if() tanpa perbandingan.

if ( $value )
{
    Do-Something
}

Jika nilainya adalah $null, ini dievaluasi ke $false. Ini mudah dibaca, tetapi berhati-hatilah karena mencari apa yang Anda harapkan untuk dicari. Saya membaca baris kode itu sebagai:

Jika $value memiliki nilai.

Tapi itu bukan seluruh cerita. Garis itu sebenarnya mengatakan:

Jika $value bukan $null atau 0 atau atau $false string kosong atau array kosong.

Berikut adalah sampel pernyataan yang lebih lengkap.

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
}

Sangat BAIK untuk menggunakan pemeriksaan dasar if selama Anda mengingat nilai-nilai lain dihitung sebagai $false dan bukan hanya variabel yang memiliki nilai.

Saya mengalami masalah ini saat merefaktor beberapa kode beberapa hari yang lalu. Ini memiliki pemeriksaan properti dasar seperti ini.

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

Saya ingin menetapkan nilai ke properti objek hanya jika ada. Dalam kebanyakan kasus, objek asli memiliki nilai yang akan dievaluasi dalam $trueif pernyataan . Tapi saya mengalami masalah di mana nilainya kadang-kadang tidak ditetapkan. Saya men-debug kode dan menemukan bahwa objek memiliki properti tetapi itu adalah nilai string kosong. Ini mencegahnya diperbarui dengan logika sebelumnya. Jadi saya menambahkan pemeriksaan yang tepat $null dan semuanya bekerja.

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

Ini adalah bug kecil seperti ini yang sulit ditemukan dan membuat saya secara agresif memeriksa nilai untuk $null.

$null. Menghitung

Jika Anda mencoba mengakses properti pada $null nilai, properti tersebut juga $null. Properti count adalah pengecualian untuk aturan ini.

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

Ketika Anda memiliki $null nilai, maka count adalah 0. Properti khusus ini ditambahkan oleh PowerShell.

[PSCustomObject] Menghitung

Hampir semua objek di PowerShell memiliki properti hitungan tersebut. Salah satu pengecualian penting adalah [PSCustomObject] di Windows PowerShell 5.1 (Ini diperbaiki di PowerShell 6.0). Ini tidak memiliki properti hitungan sehingga Anda mendapatkan $null nilai jika Anda mencoba menggunakannya. Saya menyebutnya di sini sehingga Anda tidak mencoba untuk menggunakan .Count alih-alih $null cek.

Menjalankan contoh ini pada Windows PowerShell 5.1 dan PowerShell 6.0 memberi Anda hasil yang berbeda.

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

Kosongkan null

Ada satu jenis $null khusus yang bertindak berbeda dari yang lain. Saya akan menyebutnya kosong $null tetapi itu benar-benar System.Management.Automation.Internal.AutomationNull. $null Kosong ini adalah yang Anda dapatkan sebagai hasil dari fungsi atau blok skrip yang tidak mengembalikan apa pun (hasil yang batal).

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

Jika Anda membandingkannya dengan $null, Anda mendapatkan $null nilai. Ketika digunakan dalam evaluasi di mana nilai diperlukan, nilainya selalu $null. Tetapi jika Anda menempatkannya di dalam array, itu diperlakukan sama dengan array kosong.

PS> $containempty = @( @() )
PS> $containnothing = @($nothing)
PS> $containnull = @($null)

PS> $containempty.count
0
PS> $containnothing.count
0
PS> $containnull.count
1

Anda dapat memiliki array yang berisi satu $null nilai dan count adalah 1. Tetapi jika Anda menempatkan hasil kosong di dalam array maka itu tidak dihitung sebagai item. Hitungannya adalah 0.

Jika Anda memperlakukan yang kosong $null seperti koleksi, maka itu kosong.

Jika Anda meneruskan nilai kosong ke parameter fungsi yang tidak ditik dengan kuat, PowerShell memaksakan nilai apa pun ke dalam $null nilai secara default. Ini berarti di dalam fungsi , nilai akan diperlakukan sebagai $nullalih-alih jenis System.Management.Automation.Internal.AutomationNull .

Alur

Tempat utama yang Anda lihat perbedaannya adalah saat menggunakan alur. Anda dapat menyalurkan $null nilai tetapi bukan nilai kosong $null .

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

Bergantung pada kode Anda, Anda harus mempertanyakan $null dalam logika Anda.

Periksa terlebih $null dahulu

  • Memfilter null pada alur (... | Where {$null -ne $_} | ...)
  • Menanganinya dalam fungsi alur

foreach

Salah satu fitur foreach favorit saya adalah tidak menghitung $null koleksi.

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

Ini menyelamatkan saya dari keharusan $null memeriksa koleksi sebelum saya menghitungnya. Jika Anda memiliki kumpulan $null nilai, $node masih bisa $null.

Foreach mulai bekerja dengan cara ini dengan PowerShell 3.0. Jika Anda kebetulan berada di versi yang lebih lama, maka ini tidak terjadi. Ini adalah salah satu perubahan penting yang perlu diperhatikan ketika kode port kembali untuk kompatibilitas 2.0.

Jenis nilai

Secara teknis, hanya jenis referensi yang bisa $null. Tetapi PowerShell sangat murah hati dan memungkinkan variabel untuk menjadi jenis apa pun. Jika Anda memutuskan untuk mengetik jenis nilai dengan kuat, itu tidak boleh $null. PowerShell dikonversi $null ke nilai default untuk banyak jenis.

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

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

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

Ada beberapa jenis yang tidak memiliki konversi yang valid dari $null. Jenis-jenis ini menghasilkan Cannot convert null to type kesalahan.

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

Parameter fungsi

Menggunakan nilai yang sangat ditik dalam parameter fungsi sangat umum. Kita umumnya belajar untuk mendefinisikan jenis parameter kita bahkan jika kita cenderung tidak menentukan jenis variabel lain dalam skrip kita. Anda mungkin sudah memiliki beberapa variabel yang ditik dengan kuat dalam fungsi Anda dan bahkan tidak menyadarinya.

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

Segera setelah Anda mengatur jenis parameter sebagai string, nilainya tidak pernah bisa $null. Adalah umum untuk memeriksa apakah nilai adalah $null untuk melihat apakah pengguna memberikan nilai atau tidak.

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

$Value adalah string '' kosong ketika tidak ada nilai yang disediakan. Gunakan variabel $PSBoundParameters.Value otomatis sebagai gantinya.

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

$PSBoundParameters hanya berisi parameter yang ditentukan ketika fungsi dipanggil. Anda juga dapat menggunakan ContainsKey metode untuk memeriksa properti .

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

IsNotNullOrEmpty

Jika nilainya adalah string, Anda dapat menggunakan fungsi string statis untuk memeriksa apakah nilainya adalah $null atau string kosong secara bersamaan.

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

Saya sering menggunakan ini ketika saya tahu jenis nilainya harus berupa string.

Ketika saya $null memeriksa

Saya seorang scripter defensif. Setiap kali saya memanggil fungsi dan menetapkannya ke variabel, saya memeriksanya untuk $null.

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

Saya lebih suka menggunakan if atau foreach lebih menggunakan try/catch. Jangan salah, aku masih sering menggunakannya try/catch . Tetapi jika saya dapat menguji kondisi kesalahan atau serangkaian hasil kosong, saya dapat mengizinkan penanganan pengecualian saya untuk pengecualian yang sebenarnya.

Saya juga cenderung memeriksa $null sebelum saya mengindeks ke dalam nilai atau metode panggilan pada objek. Kedua tindakan ini gagal untuk objek $null sehingga saya merasa penting untuk memvalidasinya terlebih dahulu. Aku sudah membahas skenario-skenario sebelumnya di pos ini.

Tidak ada skenario hasil

Penting untuk mengetahui bahwa fungsi dan perintah yang berbeda menangani skenario tidak ada hasil secara berbeda. Banyak perintah PowerShell mengembalikan kosong $null dan kesalahan dalam aliran kesalahan. Tetapi orang lain melemparkan pengecualian atau memberi Anda objek status. Anda masih perlu mengetahui bagaimana perintah yang Anda gunakan menangani skenario tanpa hasil dan kesalahan.

Menginisialisasi ke $null

Salah satu kebiasaan yang saya ambil adalah menginisialisasi semua variabel saya sebelum saya menggunakannya. Anda diharuskan melakukan ini dalam bahasa lain. Di bagian atas fungsi saya atau saat saya memasukkan perulangan foreach, saya menentukan semua nilai yang saya gunakan.

Berikut adalah skenario yang saya ingin Anda untuk melihat lebih dekat. Ini contoh bug yang harus kukejar sebelumnya.

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

Harapan di sini adalah bahwa Get-Something mengembalikan hasil atau kosong $null. Jika ada kesalahan, kami mencatatnya. Kemudian kami memeriksa untuk memastikan kami mendapatkan hasil yang valid sebelum memprosesnya.

Bug yang bersembunyi dalam kode ini adalah ketika Get-Something melemparkan pengecualian dan tidak menetapkan nilai ke $result. Ini gagal sebelum penugasan sehingga kita bahkan tidak menetapkan $null ke $result variabel. $result masih berisi valid $result sebelumnya dari iterasi lain. Update-Something untuk mengeksekusi beberapa kali pada objek yang sama dalam contoh ini.

Saya mengatur $result ke $null kanan di dalam perulangan foreach sebelum saya menggunakannya untuk mengurangi masalah ini.

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

Masalah cakupan

Ini juga membantu mengurangi masalah cakupan. Dalam contoh itu, kami menetapkan nilai untuk $result berulang-ulang dalam perulangan. Tetapi karena PowerShell memungkinkan nilai variabel dari luar fungsi berdarah ke dalam cakupan fungsi saat ini, menginisialisasinya di dalam fungsi Anda mengurangi bug yang dapat diperkenalkan seperti itu.

Variabel yang tidak diinisialisasi dalam fungsi Anda bukan $null jika diatur ke nilai dalam cakupan induk. Cakupan induk bisa menjadi fungsi lain yang memanggil fungsi Anda dan menggunakan nama variabel yang sama.

Jika saya mengambil contoh yang sama Do-something dan menghapus perulangan, saya akan berakhir dengan sesuatu yang terlihat seperti contoh ini:

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

Jika panggilan untuk Get-Something melempar pengecualian, maka pemeriksaan saya $null menemukan $result dari Invoke-Something. Menginisialisasi nilai di dalam fungsi Anda mengurangi masalah ini.

Penamaan variabel sulit dan umum bagi penulis untuk menggunakan nama variabel yang sama dalam beberapa fungsi. Aku tahu aku menggunakan $node,$result ,$data sepanjang waktu . Jadi akan sangat mudah untuk nilai dari cakupan yang berbeda untuk muncul di tempat-tempat di mana mereka seharusnya tidak.

Alihkan output ke $null

Saya telah berbicara tentang $null nilai-nilai untuk seluruh artikel ini tetapi topik ini tidak lengkap jika saya tidak menyebutkan pengalihan output ke $null. Ada kalanya Anda memiliki perintah yang menghasilkan informasi atau objek yang ingin Anda tekan. Mengalihkan output untuk $null melakukannya.

Out-Null

Perintah Out-Null adalah cara bawaan untuk mengalihkan data alur ke $null.

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

Tetapkan ke $null

Anda dapat menetapkan hasil perintah untuk $null efek yang sama seperti menggunakan Out-Null.

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

Karena $null merupakan nilai konstanta, Anda tidak pernah dapat menimpanya. Saya tidak suka tampilannya dalam kode saya tetapi sering berkinerja lebih cepat daripada Out-Null.

Alihkan ke $null

Anda juga dapat menggunakan operator pengalihan untuk mengirim output ke $null.

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

Jika Anda berurusan dengan executable baris perintah yang menghasilkan pada aliran yang berbeda. Anda dapat mengalihkan semua aliran output ke $null seperti ini:

git status *> $null

Ringkasan

Saya membahas banyak dasar yang satu ini dan saya tahu artikel ini lebih terfragmentasi daripada sebagian besar penyelaman mendalam saya. Itu karena $null nilai dapat muncul di banyak tempat berbeda di PowerShell dan semua nuansa khusus untuk tempat Anda menemukannya. Saya harap Anda menjauh dari ini dengan pemahaman $null yang lebih baik dan kesadaran tentang skenario yang lebih tidak jelas yang mungkin Anda alami.