Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
PowerShell $null
sembra spesso semplice, ma presenta molte sfumature. Esaminiamo da vicino $null
per sapere cosa succede quando ci si imbatte in modo imprevisto in un valore $null
.
Annotazioni
La versione originale di questo articolo è apparsa sul blog scritto da @KevinMarquette. Il team di PowerShell ringrazia Kevin per aver condiviso questo contenuto con noi. È possibile visitare il suo blog all'indirizzo PowerShellExplained.com.
Che cos'è NULL?
È possibile considerare NULL come un valore sconosciuto o vuoto. Una variabile è NULL finché non si assegna un valore o un oggetto. Questo può essere importante perché esistono alcuni comandi che richiedono un valore e generano errori se il valore è NULL.
PowerShell $null
$null
è una variabile automatica in PowerShell usata per rappresentare NULL. È possibile assegnarlo alle variabili, usarlo nei confronti e usarlo come segnaposto per NULL in una raccolta.
PowerShell considera $null
come oggetto un valore NULL. Questo è diverso da quello che ci si può aspettare se si proviene da un'altra lingua.
Esempi di $null
Ogni volta che si tenta di usare una variabile non inizializzata, il valore è $null
. Si tratta di uno dei modi più comuni in cui i valori $null
si insinuano nel codice.
PS> $null -eq $undefinedVariable
True
Se si digita erroneamente un nome di variabile, PowerShell lo vede come variabile diversa e il valore è $null
.
L'altro modo in cui si trovano $null
i valori è quando provengono da altri comandi che non forniscono risultati.
PS> function Get-Nothing {}
PS> $value = Get-Nothing
PS> $null -eq $value
True
Impatto di $null
$null
i valori influiscono sul codice in modo diverso a seconda della posizione in cui vengono visualizzati.
In stringhe
Se si usa $null
in una stringa, si tratta di un valore vuoto (o di una stringa vuota).
PS> $value = $null
PS> Write-Output "'The value is $value'"
'The value is '
Questo è uno dei motivi per cui mi piace posizionare le parentesi intorno alle variabili quando vengono usate nei messaggi di log. È ancora più importante identificare i bordi dei valori delle variabili quando il valore si trova alla fine della stringa.
PS> $value = $null
PS> Write-Output "The value is [$value]"
The value is []
In questo modo, stringhe vuote e $null
valori sono facili da individuare.
In un'equazione numerica
Quando un $null
valore viene usato in un'equazione numerica, i risultati non sono validi se non generano un errore. A volte $null
si valuta a 0
e altre volte rende l'intero risultato $null
.
Di seguito è riportato un esempio con moltiplicazione che fornisce 0 o $null
a seconda dell'ordine dei valori.
PS> $null * 5
PS> $null -eq ( $null * 5 )
True
PS> 5 * $null
0
PS> $null -eq ( 5 * $null )
False
Al posto di una raccolta
Una raccolta consente di usare un indice per accedere ai valori. Se si tenta di indicizzare in una raccolta che è effettivamente null
, viene visualizzato questo errore: 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
Se si dispone di una raccolta ma si tenta di accedere a un elemento non incluso nella raccolta, si ottiene un $null
risultato.
$array = @( 'one','two','three' )
$null -eq $array[100]
True
Al posto di un oggetto
Se si tenta di accedere a una proprietà o a una proprietà secondaria di un oggetto che non dispone della proprietà specificata, si ottiene un $null
valore simile a quello di una variabile non definita. Non importa se la variabile è $null
o un oggetto effettivo in questo caso.
PS> $null -eq $undefined.Some.Fake.Property
True
PS> $date = Get-Date
PS> $null -eq $date.Some.Fake.Property
True
Metodo su un'espressione con valori Null
La chiamata a un metodo su un oggetto $null
provoca un'eccezione 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
Ogni volta che vedo la frase You cannot call a method on a null-valued expression
, la prima cosa che cerco sono i luoghi in cui si chiama un metodo su una variabile senza prima controllarlo per $null
.
Verifica della presenza di $null
Potresti aver notato che ho sempre posizionato $null
a sinistra quando controllo per $null
nei miei esempi. Questa operazione è intenzionale e accettata come procedura consigliata di PowerShell. Esistono alcuni scenari in cui posizionarlo a destra non fornisce il risultato previsto.
Esaminare questo esempio successivo e provare a stimare i risultati:
if ( $value -eq $null )
{
'The array is $null'
}
if ( $value -ne $null )
{
'The array is not $null'
}
Se non si definisce $value
, il primo restituisce $true
e il messaggio è The array is $null
. La trappola qui è che è possibile creare un $value
che consente a entrambi di essere $false
$value = @( $null )
In questo caso, $value
è una matrice che contiene un oggetto $null
. Verifica -eq
ogni valore nella matrice e restituisce l'oggetto $null
che corrisponde. Il risultato è $false
. Restituisce tutto ciò che non corrisponde a -ne
, quindi in questo caso non ci sono risultati (viene valutato anche come $null
). Nessuno dei due è $true
anche se sembra che uno di essi dovrebbe esserlo.
Non solo è possibile creare un valore che fa sì che entrambi i valori siano valutati come $false
, ma è anche possibile creare un valore in cui entrambi siano valutati come $true
. Mathias Jessen (@IISResetMe) ha un buon post che si immerge in questo scenario.
PSScriptAnalyzer e VS Code
Il modulo PSScriptAnalyzer ha una regola che verifica la presenza di questo problema denominato PSPossibleIncorrectComparisonWithNull
.
PS> Invoke-ScriptAnalyzer ./myscript.ps1
RuleName Message
-------- -------
PSPossibleIncorrectComparisonWithNull $null should be on the left side of equality comparisons.
Poiché VS Code usa anche le regole PSScriptAnalyser, evidenzia o lo identifica come un problema nello script.
Controllo semplice se
Un modo comune in cui gli utenti controllano la presenza di un valore non $null consiste nell'usare una semplice if()
istruzione senza il confronto.
if ( $value )
{
Do-Something
}
Se il valore è $null
, restituisce $false
. Questo è facile da leggere, ma prestare attenzione che stia cercando esattamente quello che ci si aspetta di cercare. Ho letto la riga di codice come:
Se
$value
ha un valore.
Ma non è tutta la storia. Quella riga sta effettivamente dicendo:
Se
$value
non è$null
,0
,$false
, una stringa vuota o una matrice vuota.
Di seguito è riportato un esempio più completo di tale istruzione.
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
}
È perfettamente ok usare un controllo di base if
purché si ricordino questi altri valori come $false
e non solo che una variabile ha un valore.
Si è verificato questo problema durante il refactoring di codice qualche giorno fa. Aveva un controllo di proprietà di base come questo.
if ( $object.Property )
{
$object.Property = $value
}
Volevo assegnare un valore alla proprietà dell'oggetto solo se esistesse. Nella maggior parte dei casi, l'oggetto originale ha un valore che restituisce $true
nell'istruzione if
. Ma si è verificato un problema a causa del quale il valore non veniva occasionalmente impostato. È stato eseguito il debug del codice e si è scoperto che l'oggetto aveva la proprietà, ma era un valore stringa vuoto. In questo modo non è mai stato possibile aggiornarlo con la logica precedente. Quindi ho aggiunto un controllo corretto $null
e tutto ha funzionato.
if ( $null -ne $object.Property )
{
$object.Property = $value
}
Sono piccoli bug come questi che sono difficili da individuare e mi fanno controllare in modo aggressivo i valori per $null
.
$null. Contare
Se tenti di accedere a una proprietà di un valore $null
, anche questa proprietà è $null
. La Count
proprietà è l'eccezione a questa regola.
PS> $value = $null
PS> $value.Count
0
Quando si ha un valore di $null
, l'oggetto Count
è 0
. Questa proprietà speciale viene aggiunta da PowerShell.
[PSCustomObject] Contare
Quasi tutti gli oggetti in PowerShell dispongono di tale Count
proprietà. Un'eccezione importante è rappresentata da [pscustomobject]
in Windows PowerShell 5.1 (questo problema è risolto in PowerShell 6.0). Non ha una Count
proprietà, quindi ricevi un $null
valore se provi a usarlo. Lo sottolineo qui in modo che non provi a usare Count
invece di un controllo $null
.
L'esecuzione di questo esempio in Windows PowerShell 5.1 e PowerShell 6.0 offre risultati diversi.
$value = [pscustomobject]@{Name='MyObject'}
if ( $value.Count -eq 1 )
{
"We have a value"
}
Enumerabile nullo
C'è un tipo speciale di $null
che agisce in modo diverso rispetto agli altri. Lo chiamerò null enumerabile, ma è davvero un System.Management.Automation.Internal.AutomationNull.
Questo valore Null enumerabile è quello che si ottiene come risultato di una funzione o di un blocco di script che non restituisce nulla (risultato void).
PS> function Get-Nothing {}
PS> $nothing = Get-Nothing
PS> $null -eq $nothing
True
Se si confronta con $null
, si ottiene un $null
valore. Se usato in una valutazione in cui è necessario un valore, il valore è sempre $null
. Ma se la si inserisce all'interno di una matrice, viene trattata come una matrice vuota.
PS> $containEmpty = @( @() )
PS> $containNothing = @($nothing)
PS> $containNull = @($null)
PS> $containEmpty.Count
0
PS> $containNothing.Count
0
PS> $containNull.Count
1
È possibile avere una matrice che contiene un $null
valore e il relativo Count
è 1
. Tuttavia, se si inserisce una matrice vuota all'interno di una matrice, non viene conteggiata come elemento. Il conteggio è 0
.
Se si considera null enumerabile come una raccolta, il valore è vuoto.
Se si passa un enumerabile null a un parametro di funzione che non è fortemente tipizzato, PowerShell trasforma l'enumerabile null in un valore $null
per impostazione predefinita. Ciò significa che all'interno della funzione il valore viene considerato come $null
anziché il tipo System.Management.Automation.Internal.AutomationNull .
Oleodotto
La posizione principale in cui si vede la differenza è quando si usa la pipeline. È possibile inviare tramite pipe un $null
valore ma non un valore Null enumerabile.
PS> $null | ForEach-Object{ Write-Output 'NULL Value' }
'NULL Value'
PS> $nothing | ForEach-Object{ Write-Output 'No Value' }
A seconda del codice, è necessario tenere conto di $null
nella logica.
Verificare prima la presenza $null
di
- Filtra i valori null nella pipeline (
... | where {$null -ne $_} | ...
) - Gestiscilo nella funzione della pipeline
foreach
Una delle mie caratteristiche preferite di foreach
è che non enumera su una $null
raccolta.
foreach ( $node in $null )
{
#skipped
}
In questo modo è possibile evitare di dover $null
controllare la raccolta prima di enumerarla. Se si dispone di una raccolta di $null
valori, l'oggetto $node
può comunque essere $null
.
Il foreach
ha iniziato a lavorare in questo modo con PowerShell 3.0. Se si usa una versione precedente, non è così. Si tratta di una delle modifiche importanti da essere consapevoli di quando si fa back-porting del codice per la compatibilità alla 2.0.
Tipi di valori
Tecnicamente, solo i tipi riferimento possono essere $null
. Tuttavia, PowerShell è molto generoso e consente alle variabili di essere qualsiasi tipo. Se si decide di digitare fortemente un tipo di valore, non può essere $null
.
PowerShell converte $null
in un valore predefinito per molti tipi.
PS> [int]$number = $null
PS> $number
0
PS> [bool]$boolean = $null
PS> $boolean
False
PS> [string]$string = $null
PS> $string -eq ''
True
Esistono alcuni tipi che non dispongono di una conversione valida da $null
. Questi tipi generano un Cannot convert null to type
errore.
PS> [datetime]$date = $null
Cannot convert null to type "System.DateTime".
At line:1 char:1
+ [datetime]$date = $null
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
Parametri della funzione
L'uso di valori fortemente tipizzati nei parametri della funzione è molto comune. In genere si apprenderà a definire i tipi dei parametri anche se si tende a non definire i tipi di altre variabili negli script. Potrebbero essere già presenti alcune variabili fortemente tipate nelle funzioni e non lo si rende neanche conto.
function Do-Something
{
param(
[string] $Value
)
}
Non appena si imposta il tipo del parametro come , string
il valore non può mai essere $null
. È comune verificare se un valore è $null
verificare se l'utente ha fornito o meno un valore.
if ( $null -ne $Value ){...}
$Value
è una stringa ''
vuota quando non viene specificato alcun valore. Usare invece la variabile $PSBoundParameters.Value
automatica.
if ( $null -ne $PSBoundParameters.Value ){...}
$PSBoundParameters
contiene solo i parametri specificati quando è stata chiamata la funzione .
È anche possibile utilizzare il ContainsKey
metodo per verificare la proprietà .
if ( $PSBoundParameters.ContainsKey('Value') ){...}
IsNotNullOrEmpty
Se il valore è una stringa, è possibile usare una funzione stringa statica per verificare se il valore è $null
o una stringa vuota contemporaneamente.
if ( -not [string]::IsNullOrEmpty( $value ) ){...}
Mi trovo a usare questo spesso quando so che il tipo di valore deve essere una stringa.
Quando effettuo un controllo $null
Sono uno scripter difensivo. Ogni volta che chiamo una funzione e la assegno a una variabile, la controllo per $null
.
$userList = Get-ADUser kevmar
if ($null -ne $userList){...}
Preferisco usare if
o foreach
più usando try/catch
. Non mi sbaglio, uso try/catch
ancora molto. Tuttavia, se è possibile verificare la presenza di una condizione di errore o un set vuoto di risultati, è possibile consentire la gestione delle eccezioni per le eccezioni vere.
Tendo anche a controllare $null
prima di indicizzare un valore o invocare metodi su un oggetto. Queste due azioni hanno esito negativo per un $null
oggetto, quindi trovo importante convalidarle per prime. Questi scenari sono già stati illustrati in precedenza in questo post.
Nessun scenario di risultati
È importante sapere che funzioni e comandi diversi gestiscono in modo diverso lo scenario senza risultati. Molti comandi di PowerShell restituiscono il valore Null enumerabile e un errore nel flusso di errori. Ma altri lanciano eccezioni o forniscono un oggetto di stato. È comunque necessario conoscere il modo in cui i comandi utilizzati gestiscono l'assenza di risultati e gli scenari di errore.
Inizializzazione a $null
Un'abitudine che ho preso è inizializzare tutte le mie variabili prima di usarle. È necessario eseguire questa operazione in altre lingue. In cima alla mia funzione o quando entro in un foreach
ciclo, definisco tutti i valori che sto utilizzando.
Ecco uno scenario che voglio che tu prenda un'occhiata da vicino. È un esempio di bug che ho dovuto inseguire in passato.
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
}
}
}
L'aspettativa è che Get-Something
restituisce un risultato o un valore Null enumerabile. Se si verifica un errore, viene registrato. Verificare quindi di avere ottenuto un risultato valido prima di elaborarlo.
Il bug nascosto in questo codice è quando Get-Something
genera un'eccezione e non assegna un valore a $result
. L'operazione ha esito negativo prima dell'assegnazione, quindi non viene nemmeno assegnata $null
alla $result
variabile.
$result
contiene ancora il precedente valido $result
dalle altre iterazioni.
Update-Something
per eseguire più volte sullo stesso oggetto in questo esempio.
Ho impostato $result
a $null
direttamente all'interno del foreach
ciclo prima di usarlo per attenuare questo problema.
foreach ( $node in 1..6 )
{
$result = $null
try
{
...
Problemi di ambito
Ciò consente anche di attenuare i problemi di ambito. In questo esempio, i valori vengono assegnati ripetutamente a $result
in un ciclo. Tuttavia, poiché PowerShell consente ai valori delle variabili esterni alla funzione di influire nell'ambito della funzione corrente, inizializzarli all'interno della funzione riduce i bug che possono essere introdotti in questo modo.
Una variabile non inizializzata nella tua funzione non è $null
se viene impostata su un valore in un ambito genitore.
L'ambito padre potrebbe essere un'altra funzione che chiama la tua funzione e usa gli stessi nomi di variabile.
Se si accetta lo stesso Do-something
esempio e si rimuove il ciclo, si ottiene un risultato simile all'esempio seguente:
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
}
}
Se la chiamata a Get-Something
genera un'eccezione, allora il mio controllo $null
trova il $result
da Invoke-Something
. L'inizializzazione del valore all'interno della funzione attenua questo problema.
La denominazione delle variabili è difficile ed è comune che un autore usi gli stessi nomi di variabile in più funzioni. So che uso $node
,$result
$data
tutto il tempo. Quindi sarebbe molto facile che i valori di ambiti diversi vengano visualizzati in posizioni in cui non dovrebbero essere.
Reindirizzare l'output a $null
Ho parlato di $null
valori per l'intero articolo, ma l'argomento non è completo se non menzionassi il reindirizzamento dell'output a $null
. In alcuni casi si dispone di comandi che generano informazioni o oggetti che si desidera eliminare. Il reindirizzamento dell'output a $null
esegue questa operazione.
Out-Null
Il comando Out-Null è il modo predefinito per reindirizzare i dati della pipeline a $null
.
New-Item -Type Directory -Path $path | Out-Null
Assegna a $null
È possibile assegnare i risultati di un comando a $null
per lo stesso effetto dell'uso Out-Null
di .
$null = New-Item -Type Directory -Path $path
Poiché $null
è un valore costante, non è mai possibile sovrascriverlo. Non mi piace il modo in cui appare nel codice, ma spesso esegue più velocemente di Out-Null
.
Reindirizzamento a $null
È anche possibile usare l'operatore di reindirizzamento per inviare l'output a $null
.
New-Item -Type Directory -Path $path > $null
Se si riguardano file eseguibili da riga di comando che generano output sui diversi flussi. È possibile reindirizzare tutti i flussi di output in questo modo: $null
git status *> $null
Riassunto
Ho trattato molti argomenti in questo caso e so che questo articolo è più frammentato della maggior parte delle mie analisi approfondite. Ciò è dovuto al fatto che $null
i valori possono essere visualizzati in molte posizioni diverse in PowerShell e tutte le sfumature sono specifiche della posizione in cui si trova. Spero che ci si allontana da questo con una migliore comprensione di $null
e una consapevolezza degli scenari più oscuri che si potrebbero affrontare.