Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
PowerShell $null
sering tampak sederhana tetapi memiliki banyak nuansa. Mari kita lihat lebih dekat pada $null
agar Anda tahu apa yang terjadi ketika Anda secara tak terduga menemui nilai $null
.
Nota
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 dalam komparasi, dan menggunakannya sebagai penanda tempat 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 kosong dan nilai $null
mudah ditemukan.
Dalam persamaan numerik
Ketika nilai $null
digunakan dalam persamaan numerik, hasil Anda tidak valid jika tidak menimbulkan kesalahan. Kadang-kadang $null
mengevaluasi ke 0
, dan di lain waktu membuat hasil keseluruhannya menjadi $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, maka Anda mendapatkan hasil berupa $null
.
$array = @( 'one','two','three' )
$null -eq $array[100]
True
Sebagai pengganti 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 objek $null
melemparkan 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 akan dievaluasi menjadi $true
dan pesan kita menjadi 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
.
-eq
memeriksa setiap nilai dalam array dan $null
yang cocok akan dikembalikan. Nilainya menjadi $false
. Kombinasi -ne
mengembalikan semua elemen yang tidak sesuai dengan $null
dan dalam hal ini tidak ada hasil (Ini juga dievaluasi sebagai $false
). Tidak satu pun dari mereka adalah $true
meskipun terlihat seperti salah satu seharusnya.
Kami tidak hanya dapat membuat nilai yang membuat keduanya dievaluasi ke $false
, tetapi juga memungkinkan untuk membuat nilai di mana keduanya dievaluasi ke $true
. Mathias Jessen (@IISResetMe) memiliki postingan bagus yang menyelami skenario tersebut.
PSScriptAnalyzer dan Visual Studio Code
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 VS Code juga menggunakan aturan PSScriptAnalyser, maka ini juga menyoroti atau mengidentifikasi hal 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 waspadailah bahwa ia mencari tepat apa yang Anda harapkan. Saya membaca baris kode tersebut sebagai:
Jika
$value
memiliki nilai.
Tapi itu bukan seluruh cerita. Baris itu benar-benar mengatakan:
Jika
$value
bukan$null
atau0
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
}
Sungguh boleh menggunakan uji dasar if
selama Anda mengingat bahwa nilai-nilai lain dianggap sebagai $false
dan bukan hanya bahwa variabel memiliki nilai.
Saya mengalami masalah ini ketika merefaktor beberapa kode beberapa hari yang lalu. Ini memiliki pengecekan 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
pada pernyataan if
. Tapi aku 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. Hal ini mencegahnya diperbarui dengan logika sebelumnya. Jadi saya menambahkan pemeriksaan yang semestinya $null
dan semuanya berfungsi.
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. Hitung
Jika Anda mencoba mengakses properti pada nilai $null
, maka properti tersebut adalah $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] Jumlah
Hampir semua objek di PowerShell memiliki properti tersebut Count
. Salah satu pengecualian penting adalah [pscustomobject]
di Windows PowerShell 5.1 (Ini diperbaiki di PowerShell 6.0). Ini tidak memiliki Count
properti sehingga Anda mendapatkan $null
nilai jika Anda mencoba menggunakannya. Saya sampaikan ini di sini agar Anda tidak mencoba menggunakan Count
alih-alih pemeriksaan $null
.
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"
}
Enumerasi tanpa nilai
Ada satu jenis $null
khusus yang bertindak berbeda dari yang lain. Saya akan menyebutnya null yang dapat diiterasi 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. Jumlahnya adalah 0
.
Jika Anda memperlakukan null yang dapat dijumlahkan seperti koleksi, maka kosong.
Jika Anda meneruskan enumerable null ke parameter fungsi yang bukan tipe data yang kuat, PowerShell secara otomatis mengubah enumerable null tersebut menjadi nilai $null
. Ini berarti di dalam fungsi, nilai tersebut diperlakukan sebagai tipe lain bukan sebagai jenis $null
.
Jaringan pipa
Tempat utama yang Anda lihat perbedaannya adalah saat menggunakan alur. Anda dapat menyalurkan $null
nilai tetapi bukan nilai null yang enumerabel.
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
- Menyaring null pada pipa alur (
... | where {$null -ne $_} | ...
) - Menanganinya dalam fungsi alur
foreach (pengulangan untuk setiap elemen)
Salah satu fitur foreach
favorit saya adalah bahwa itu tidak melakukan iterasi pada 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
Mulai bekerja dengan cara ini pada foreach
di 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 menetapkan tipe nilai secara tegas, 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 dengan tipe data yang kuat 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 bertipe 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 penulis 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 diiterasi 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 adalah contoh bug yang harus saya perbaiki 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 diiterasi. 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 mengatur $null
ke variabel $result
.
$result
masih berisi $result
sebelumnya yang valid dari iterasi lain.
Update-Something
untuk menjalankan beberapa kali pada objek yang sama dalam contoh ini.
Saya mengatur $result
ke $null
tepat di dalam perulangan foreach
sebelum menggunakannya untuk mengatasi 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 sembunyikan. Mengalihkan output ke $null
akan melakukan hal tersebut.
Out-Null (mengabaikan output)
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 dari perintah ke $null
untuk mendapatkan efek yang sama seperti menggunakan Out-Null
.
$null = New-Item -Type Directory -Path $path
Karena $null
merupakan nilai konstanta, Anda tidak dapat menggantikannya. 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 topik dan saya tahu artikel ini lebih terfragmentasi dibandingkan dengan kebanyakan penelitian mendalam saya. Itu karena nilai $null
dapat muncul di berbagai tempat di PowerShell dan nuansanya sangat bergantung pada tempat ditemukan. Saya harap Anda menjauh dari ini dengan pemahaman $null
yang lebih baik dan kesadaran tentang skenario yang lebih tidak jelas yang mungkin Anda alami.