Bagikan melalui


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 place holder 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 ketik nama variabel, PowerShell melihatnya sebagai variabel yang berbeda dan nilainya adalah $null.

Cara lain Anda menemukan $null nilai adalah ketika nilai tersebut 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 nilai 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 siku di sekitar variabel saat menggunakannya dalam pesan log. Lebih penting lagi untuk mengidentifikasi tepi nilai variabel Anda saat 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 ditemukan.

Dalam persamaan numerik

$null Ketika nilai digunakan dalam persamaan numerik, hasil Anda tidak valid jika tidak memberikan kesalahan. $null Kadang-kadang mengevaluasi ke 0 dan di lain waktu itu membuat seluruh hasilnya $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 jika 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 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 saat 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 mengevaluasi ke $true dan pesan kami adalah The array is $null. Perangkap di sini adalah bahwa adalah mungkin untuk membuat $value yang memungkinkan keduanya 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 mereka berdua mengevaluasi ke $true. Mathias Jessen (@IISResetMe) memiliki postingan bagus yang menyelami skenario tersebut.

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 bahwa orang memeriksa nilai non-$null adalah dengan menggunakan pernyataan sederhana if() tanpa perbandingan.

if ( $value )
{
    Do-Something
}

Jika nilainya adalah $null, ini mengevaluasi ke $false. Ini mudah dibaca, tetapi berhati-hatilah bahwa ia mencari persis apa yang Anda harapkan untuk dicari. Saya membaca baris kode tersebut sebagai:

Jika $value memiliki nilai.

Tapi itu bukan seluruh cerita. Baris itu benar-benar mengatakan:

Jika $value bukan $null atau 0 atau $false atau 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 OK 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 ketika 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 $true dalam if pernyataan. Tapi aku mengalami masalah di mana nilainya kadang-kadang tidak diatur. Saya men-debug kode dan menemukan bahwa objek memiliki properti tetapi itu adalah nilai string kosong. Hal 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 di 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"
}

Enumerable null

Ada satu jenis $null khusus yang bertindak berbeda dari yang lain. Saya akan menyebutnya null yang dapat dijumlahkan tetapi itu benar-benar System.Management.Automation.Internal.AutomationNull. Null yang dapat dijumlahkan 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. Saat 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 nilainya count adalah 1. Tetapi jika Anda menempatkan array kosong di dalam array, maka array tersebut tidak dihitung sebagai item. Hitungannya adalah 0.

Jika Anda memperlakukan null yang dapat dijumlahkan seperti koleksi, maka kosong.

Jika Anda meneruskan null yang dapat dijumlahkan ke parameter fungsi yang tidak ditik dengan kuat, PowerShell memaksa null yang dapat dijumlahkan ke dalam $null nilai secara default. Ini berarti di dalam fungsi, nilai diperlakukan sebagai $null alih-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 null yang dapat dijumlahkan.

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

Bergantung pada kode Anda, Anda harus memperhitungkan $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 bahwa itu tidak menghitung koleksi $null .

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

Ini menyelamatkan saya dari harus $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 dapat berupa $null. Tetapi PowerShell sangat murah hati dan memungkinkan variabel menjadi jenis apa pun. Jika Anda memutuskan untuk mengetik jenis nilai dengan kuat, itu tidak boleh $null. PowerShell mengonversi $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 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 mendefinisikan jenis parameter kita bahkan jika kita cenderung tidak menentukan jenis variabel lain dalam skrip kita. Anda mungkin sudah memiliki beberapa variabel yang sangat ditik 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 skrip 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 benar.

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

Tidak ada skenario hasil

Penting untuk diketahui bahwa berbagai fungsi dan perintah menangani skenario tidak ada hasil secara berbeda. Banyak perintah PowerShell mengembalikan null yang dapat dijumlahkan dan kesalahan dalam aliran kesalahan. Tetapi orang lain melemparkan pengecualian atau memberi Anda objek status. Masih terserah Anda untuk mengetahui bagaimana perintah yang Anda gunakan menangani skenario tidak ada 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 null yang dapat dijumlahkan. 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 kami bahkan tidak menetapkan $null ke $result variabel. $result masih berisi valid $result sebelumnya dari iterasi lain. Update-Something untuk menjalankan 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 kali dalam perulangan. Tetapi karena PowerShell memungkinkan nilai variabel dari luar fungsi untuk 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 melemparkan 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 bagi nilai dari cakupan yang berbeda untuk muncul di tempat-tempat di mana mereka seharusnya tidak berada.

Mengalihkan output ke $null

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

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 bisa menimpanya. Saya tidak suka tampilannya dalam kode saya tetapi sering berkinerja lebih cepat daripada Out-Null.

Mengalihkan 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 tentang hal ini dan saya tahu artikel ini lebih terfragmentasi daripada sebagian besar penyelaman mendalam saya. Itu karena $null nilai dapat muncul di berbagai tempat 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.