您想要瞭解$null
PowerShell $null
通常看起來很簡單,但有許多細微差別。 讓我們仔細看看 $null
,讓您知道當您意外遇到 $null
值時會發生什麼事。
注意
本文的原始版本出現在@KevinMarquette撰寫的部落格上。 PowerShell 小組感謝 Kevin 與我們分享此內容。 請查看他在 PowerShellExplained.com 的部落格。
什麼是 NULL?
您可以將 NULL 視為未知或空白值。 變數是 NULL,直到您將值或物件指派給它為止。 這很重要,因為有些命令需要值,如果值為 NULL,就會產生錯誤。
PowerShell $null
$null
是 PowerShell 中用來表示 NULL 的自動變數。 您可以將它指派給變數、在比較中使用它,並將它當做集合中 NULL 的位置持有者使用。
PowerShell 會將 $null
視為值為 NULL 的物件。 如果您來自另一種語言,這與您預期的情況不同。
$null範例
每當您嘗試使用尚未初始化的變數時,值會是 $null
。 這是值潛入程式代碼的最常見方式 $null
之一。
PS> $null -eq $undefinedVariable
True
如果您碰巧誤寫變數名稱,則 PowerShell 會將它視為不同的變數,且值為 $null
。
另一種方式是 $null
,當這些值來自未提供任何結果的其他命令時。
PS> function Get-Nothing {}
PS> $value = Get-Nothing
PS> $null -eq $value
True
$null的影響
$null
值會根據程式代碼顯示的位置而有所不同。
在字串中
如果您在 $null
字串中使用,則它是空白值(或空字串)。
PS> $value = $null
PS> Write-Output "The value is $value"
The value is
這是我在記錄訊息中使用括弧時,將括弧放在變數周圍的原因之一。 當值位於字串結尾時,識別變數值的邊緣就更加重要。
PS> $value = $null
PS> Write-Output "The value is [$value]"
The value is []
這可讓您輕鬆地找出空字串和 $null
值。
在數值方程式中
$null
當數值方程式中使用某個值時,如果結果未提供錯誤,則結果會無效。 有時候 會 $null
評估 為 0
,而其他時候,它會讓整個結果 $null
成為 。
以下是乘法的範例,會根據值的順序提供 0 或 $null
。
PS> $null * 5
PS> $null -eq ( $null * 5 )
True
PS> 5 * $null
0
PS> $null -eq ( 5 * $null )
False
取代集合
集合可讓您使用索引來存取值。 如果您嘗試將索引編製至實際 null
為 的集合,您會收到此錯誤: 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
如果您有集合,但嘗試存取不在集合中的專案,您會收到 $null
結果。
$array = @( 'one','two','three' )
$null -eq $array[100]
True
取代物件
如果您嘗試存取沒有指定屬性之物件的屬性或子屬性,您會收到一個 $null
值,就像針對未定義的變數一樣。 在此情況下,變數是否為 $null
或實際物件並不重要。
PS> $null -eq $undefined.some.fake.property
True
PS> $date = Get-Date
PS> $null -eq $date.some.fake.property
True
Null 值表示式上的方法
在物件上 $null
呼叫 方法會 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
每當我看到片語You cannot call a method on a null-valued expression
時,我尋找的第一件事就是在變數上呼叫方法的位置,而不需要先檢查它。$null
檢查$null
您可能已經注意到,在我的範例中檢查$null
時,我總是將 放在$null
左邊。 這是刻意並接受為PowerShell最佳做法。 在某些情況下,將它放在右側並不會提供您預期的結果。
請查看下一個範例,並嘗試預測結果:
if ( $value -eq $null )
{
'The array is $null'
}
if ( $value -ne $null )
{
'The array is not $null'
}
如果我未定義 $value
,則第一個評估為 $true
,而我們的訊息為 The array is $null
。 這裡的陷阱是,可以建立 $value
允許兩者成為的 $false
$value = @( $null )
在此情況下, $value
是包含的 $null
陣列。 會 -eq
檢查陣列中的每個值,並傳 $null
回相符的 。 這會評估為 $false
。 會 -ne
傳回不符合 $null
的所有專案,在此情況下沒有任何結果(這也會評估為 $false
)。 即使兩者 $true
看起來都不應該如此。
我們不僅能夠建立一個值,讓兩者都評估為 $false
,而且可以建立兩者都評估為 $true
的值。 馬蒂亞斯·傑森(@IISResetMe)有一個很好的 帖子 ,深入探討這個案例。
PSScriptAnalyzer 和 VSCode
PSScriptAnalyzer 模組有一個規則,會檢查此問題是否稱為 PSPossibleIncorrectComparisonWithNull
。
PS> Invoke-ScriptAnalyzer ./myscript.ps1
RuleName Message
-------- -------
PSPossibleIncorrectComparisonWithNull $null should be on the left side of equality comparisons.
由於 VS Code 也會使用 PSScriptAnalyser 規則,因此也會醒目提示或將它識別為腳本中的問題。
簡單檢查
人們檢查非$null值的常見方式是不使用比較的簡單 if()
語句。
if ( $value )
{
Do-Something
}
如果值為 $null
,這會評估為 $false
。 這很容易閱讀,但請小心,它正在尋找確切的期待它尋找。 我讀那行程式代碼為:
如果
$value
具有 值。
但這不是整個故事。 這一行實際上是說:
如果
$value
不是$null
或或0
或$false
,則為空字串或空陣列。
以下是該語句的更完整範例。
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
}
只要您記得其他值算作 ,$false
而不只是變數具有值,使用基本if
檢查就完全沒問題了。
幾天前重構一些程序代碼時,我遇到此問題。 它有一個基本的屬性檢查,如下所示。
if ( $object.property )
{
$object.property = $value
}
只有當物件屬性存在時,我才想要將值指派給物件屬性。 在大部分情況下,原始物件有一個值,會在 語句中評估 $true
為 if
。 但我遇到值偶爾未設定的問題。 我偵錯了程式代碼,發現物件具有 屬性,但它是空白字串值。 這可防止其使用先前的邏輯進行更新。 所以我增加了一個適當的 $null
檢查和一切工作。
if ( $null -ne $object.property )
{
$object.property = $value
}
這些小蟲子很難發現,讓我積極檢查值 $null
。
$null。計數
如果您試著存取值上的 $null
屬性,該屬性也是 $null
。 屬性 count
是此規則的例外狀況。
PS> $value = $null
PS> $value.count
0
當您有值 $null
時, count
則為 0
。 PowerShell 會新增這個特殊屬性。
[PSCustomObject]計數
PowerShell 中幾乎所有的物件都有該 count 屬性。 Windows PowerShell 5.1 中有一個重要的例外 [PSCustomObject]
狀況(這是在 PowerShell 6.0 中修正的)。 它沒有 count 屬性,因此如果您嘗試使用它, $null
就會取得值。 我在這裡稱之為 ,這樣你就不會嘗試使用 .Count
而不是 $null
檢查。
在 Windows PowerShell 5.1 和 PowerShell 6.0 上執行此範例可提供不同的結果。
$value = [PSCustomObject]@{Name='MyObject'}
if ( $value.count -eq 1 )
{
"We have a value"
}
可列舉的 Null
有一種特殊的類型,其作用方式與其他類型 $null
不同。 我要將其稱為可列舉的 Null,但它實際上是 System.Management.Automation.Internal.AutomationNull。
這個可列舉的 Null 是您取得的函式或腳本區塊的結果,該區塊不會傳回任何內容(void 結果)。
PS> function Get-Nothing {}
PS> $nothing = Get-Nothing
PS> $null -eq $nothing
True
如果您將其與 $null
比較,則會取得 $null
值。 在需要值的評估中使用時,值一律 $null
為 。 但是,如果您將它放在陣列內,則會將它視為空陣列。
PS> $containempty = @( @() )
PS> $containnothing = @($nothing)
PS> $containnull = @($null)
PS> $containempty.count
0
PS> $containnothing.count
0
PS> $containnull.count
1
您可以有包含一個 $null
值的陣列,其 count
為 1
。 但是,如果您將空陣列放在數位,則不會將其計算為專案。 計數為 0
。
如果您將可列舉的 Null 視為集合,則它是空的。
如果您將可列舉 Null 傳遞至不是強型別的函式參數,PowerShell 預設會將可列舉 Null 強制轉換成 $null
值。 這表示在函式內,值會$null
視為 System.Management.Automation.Internal.AutomationNull 類型。
管線
您看到差異的主要位置是使用管線時。 您可以使用管線傳送值,但不能使用 $null
可列舉的 Null 值。
PS> $null | ForEach-Object{ Write-Output 'NULL Value' }
'NULL Value'
PS> $nothing | ForEach-Object{ Write-Output 'No Value' }
根據您的程式代碼,您應該考慮 $null
邏輯中的 。
檢查第 $null
一個
- 篩選出管線上的 Null (
... | Where {$null -ne $_} | ...
) - 在管線函式中處理它
foreach
我最喜歡的功能 foreach
之一 $null
是它不會列舉集合。
foreach ( $node in $null )
{
#skipped
}
這樣可省去我先 $null
檢查集合,再列舉集合。 如果您有值的集合 $null
, $node
仍然可以是 $null
。
foreach 開始使用 PowerShell 3.0。 如果您碰巧是舊版,則情況並非如此。 這是 2.0 相容性後端移植程式代碼時要注意的重要變更之一。
值類型
在技術上,只有參考型別可以是 $null
。 但 PowerShell 非常慷慨,可讓變數成為任何類型的變數。 如果您決定強型別實值型別,則不能是 $null
。
PowerShell 會 $null
轉換成許多類型的預設值。
PS> [int]$number = $null
PS> $number
0
PS> [bool]$boolean = $null
PS> $boolean
False
PS> [string]$string = $null
PS> $string -eq ''
True
有些類型沒有從 $null
的有效轉換。 這些類型會產生 Cannot convert null to type
錯誤。
PS> [datetime]$date = $null
Cannot convert null to type "System.DateTime".
At line:1 char:1
+ [datetime]$date = $null
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
函數參數
在函式參數中使用強型別值是很常見的。 我們通常會瞭解如何定義參數的類型,即使我們傾向於不在我們的腳本中定義其他變數的類型。 您的函式中可能已經有一些強型別變數,甚至無法實現。
function Do-Something
{
param(
[String] $Value
)
}
您將 參數的類型設定為 string
,值就永遠不能是 $null
。 檢查值是否為 $null
查看使用者是否提供值,很常見。
if ( $null -ne $Value ){...}
$Value
當未提供任何值時,是空字串 ''
。 請改用自動變數 $PSBoundParameters.Value
。
if ( $null -ne $PSBoundParameters.Value ){...}
$PSBoundParameters
只會包含呼叫函式時所指定的參數。
您也可以使用 ContainsKey
方法來檢查 屬性。
if ( $PSBoundParameters.ContainsKey('Value') ){...}
IsNotNullOrEmpty
如果值是字串,您可以使用靜態字串函式來檢查值是否為 $null
或同時為空字串。
if ( -not [string]::IsNullOrEmpty( $value ) ){...}
當我知道實值類型應該是字串時,我經常會使用這個 。
當我$null檢查時
我是一個防禦腳本手。 每當我呼叫函式並將它指派給變數時,我就會檢查它是否有 $null
。
$userList = Get-ADUser kevmar
if ($null -ne $userList){...}
我更喜歡使用 if
或使用 foreach
try/catch
。 別搞錯了,我仍然用 try/catch
了很多。 但是,如果我可以測試錯誤條件或空的結果集,我可以允許我的例外狀況處理是針對真正的例外狀況。
我也會在索引到物件上的值或呼叫方法之前先檢查 $null
。 這兩個物件動作失敗, $null
因此我發現必須先驗證它們很重要。 我已在本文稍早涵蓋這些案例。
沒有結果案例
請務必知道不同的函式和命令會以不同的方式處理無結果案例。 許多 PowerShell 命令都會傳回可列舉的 Null,並在錯誤數據流中傳回錯誤。 但其他人會擲回例外狀況或提供狀態物件。 您必須知道您使用的指令如何處理無結果和錯誤案例。
初始化為$null
我所挑選的一個習慣是先初始化所有變數,然後再使用這些變數。 您必須以其他語言執行這項操作。 在函式頂端,或當我輸入 foreach 循環時,我定義我正在使用的所有值。
以下是我想仔細看看的案例。 這是我以前必須追趕的 Bug 的範例。
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
}
}
}
這裏的預期是傳 Get-Something
回結果或可列舉的 Null。 如果發生錯誤,我們會將其記錄。 然後,我們會檢查以確定我們在處理之前取得有效的結果。
隱藏在此程序代碼中的 Bug 是 Get-Something
擲回例外狀況,且不會將值指派給 $result
時。 它會在指派之前失敗,因此我們甚至不會指派 $null
給 $result
變數。 $result
仍然包含來自其他反覆專案的上一個有效 $result
專案。
Update-Something
表示在此範例中對相同物件執行多次。
我在 foreach 循環內設定 $result
為 $null
右方,然後再使用它來減輕此問題。
foreach ( $node in 1..6 )
{
$result = $null
try
{
...
範圍問題
這也有助於減輕範圍問題。 在此範例中,我們會在迴圈中將值指派給 $result
來回。 但是,因為PowerShell允許函式外部的變數值流入目前函式的範圍,因此在您的函式內初始化它們可減輕可透過這種方式引入的Bug。
如果您的函式中未初始化的變數設定為父範圍中的值,則不是 $null
。
父範圍可以是另一個函式,會呼叫您的函式,並使用相同的變數名稱。
如果我採用相同的 Do-something
範例並移除迴圈,最後會有類似此範例的內容:
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
擲回例外狀況,則我的 $null
檢查會 $result
從 Invoke-Something
找到 。 初始化函式內的值可減輕此問題。
命名變數很難,而且作者通常會在多個函式中使用相同的變數名稱。 我知道我一直使用 $node
。$result
。$data
因此,來自不同範圍的值很容易出現在不應該出現的地方。
將輸出重新導向至$null
我一直在討論 $null
本文的值,但如果我未提及將輸出重新導向至 $null
,主題就不會完成。 有時候,您有命令會輸出您想要隱藏的資訊或物件。 將輸出重新導向至 $null
該作業。
Out-Null
Out-Null 命令是將管線數據重新導向至 $null
的內建方式。
New-Item -Type Directory -Path $path | Out-Null
指派給$null
您可以針對與 使用 Out-Null
相同的效果,將命令的結果指派給 $null
。
$null = New-Item -Type Directory -Path $path
因為 $null
是常數值,所以永遠無法覆寫它。 我不喜歡在程式代碼中的外觀,但它的執行速度通常比 快 Out-Null
。
重新導向至$null
您也可以使用重新導向運算子將輸出傳送至 $null
。
New-Item -Type Directory -Path $path > $null
如果您正在處理在不同資料流上輸出的命令列可執行檔。 您可以將所有輸出資料流重新導向至 $null
如下:
git status *> $null
摘要
我在這個文章中涵蓋了很多問題,我知道這篇文章比我大部分的深入探討更分散。 這是因為 $null
值可以在PowerShell的許多不同位置彈出,而且所有細微差別都專屬於您找到的位置。 我希望你離開這個, 有更好的了解 $null
和意識到你可能會遇到的更模糊的案例。