Skip to main content

PowerShell Variables and Scope

Dave Mason PowerShell Variable Scope

Let's talk a little bit about PowerShell variables and how long they exist within the scopes they're defined. I've encountered some behavior that for me, was unexpected. It's made my development efforts unproductive--especially when it comes to debugging.

Here's some initial code. It declares a $Birthday variable, assigns a date to it, and outputs the value of the variable:


$Birthday = [System.DateTime]::Today
Write-Host $Birthday.ToString() -ForegroundColor Yellow

PS C:\Users\Dave> C:\My Scripts\PowerShell\Variable Test.01.ps1
11/28/2021 12:00:00 AM

So far, so good. But I don't like the variable name $Birthday. Let's refactor the code, change the variable name to $Birthdate, and re-run the script:


$Birthdate = [System.DateTime]::MinValue
Write-Host $Birthday.ToString() -ForegroundColor Yellow

PS C:\Users\Dave> C:\My Scripts\PowerShell\Variable Test.02.ps1
11/28/2021 12:00:00 AM

You may have noticed a couple of things. I changed the date value of the variable to the minimum value of a DateTime (Jan 1st, 0001). However, when I output the value, it still shows today's date. What happened? Well, I got sloppy with my refactoring. I did not rename the second occurrence of the variable.

In the current state of the script, $Birthday is neither declared nor assigned a value. There was no exception, and the variable returned a non-null value. Is that what you were expecting? We'll come back to that. For now, let's fix our mistake and re-run the script:


$Birthdate = [System.DateTime]::MinValue
Write-Host $Birthdate.ToString() -ForegroundColor Yellow

PS C:\Users\Dave> C:\My Scripts\PowerShell\Variable Test.02.ps1
1/1/0001 12:00:00 AM

There, that looks correct. The correct date is output and the $Birthday variable is completely gone from the script. Now, about that variable...is it still in scope and available in the console? In a word, yes. You can see evidence of this from Intellisense in your development tool. Here's what you'd see in the PowerShell ISE:

Dave Mason PowerShell Variable Scope

What's Hiding In The Console?

I don't know of an ideal way to programatically list out just the user-defined variables that are in scope for the current session and available in the console. There is a Get-Variable cmdlet that helps some. It's got a handfull of parameters that can narrow down the list of returned variables. But it includes variables that are defined as constants and variables owned by the system. Here is the default output (truncated for readability):


PS C:\Users\Dave> Get-Variable

Name Value
---- -----
$ MinValue
? True
^ [
args {}
Birthdate 1/1/0001 12:00:00 AM
Birthday 11/28/2021 12:00:00 AM
ConfirmPreference High
ConsoleFileName
DebugPreference SilentlyContinue
Error {}
...

You can see both the $Birthdate and $Birthday variables are in scope. To remove $Birthday from scope, there is a Remove-Variable cmdlet that works well for named variables. This line of code will do the deed:


Remove-Variable -Name Birthday

Running Get-Variable again verifies that $Birthday is not in scope and no longer exists.

If you are looking for consistent, repeatable script results (especially during the development process), the code examples above might be a little disconcerting. It might make sense to remove all user-defined variables as one of the first steps of a script. Much like its Get-Variable counterpart, I'm not seeing a way to specify just user-defined variables for the Remove-Variable cmdlet. Using a wildcard character, Remove-Variable * will attempt to remove everything. However, it returns a slew of errors, telling you read-only and system-owned variables can't be removed. You can suppress the errors to get the desired effect via Remove-Variable * -ErrorAction SilentlyContinue. It's effective, if not heavy handed.


Variables Not In Scope

By default, if you attempt to reference a variable that is not in scope, PowerShell will return a value of 0 (zero) or $Null, depending on type. Here is an example:


Write-Host "Hello"
Write-Host $UndelcaredVariable -ForegroundColor Yellow
Write-Host "World"

PS C:\Users\Dave> C:\My Scripts\PowerShell\Variable Test.03.ps1
Hello

World

PowerShell offers a Set-StrictMode cmdlet that can help. Strict mode is "off" by default, so let's set turn it on and try the previous example again:


Set-StrictMode -Version Latest
Write-Host "Hello"
Write-Host $UndelcaredVariable -ForegroundColor Yellow
Write-Host "World"

PS C:\Users\Dave> C:\My Scripts\PowerShell\Variable Test.03.ps1
Hello
The variable '$UndelcaredVariable' cannot be retrieved because it has not been set.
At C:\My Scripts\PowerShell\Variable Test.03.ps1:3 char:12
+ Write-Host $UndelcaredVariable -ForegroundColor Yellow
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (UndelcaredVariable:String) [], RuntimeException
+ FullyQualifiedErrorId : VariableIsUndefined

World

This behavior is more sensible, don't you think? If you do intend to use it, keep in mind Set-StrictMode won't save you in every scenario. A variable can be loaded into the console and in scope during your development session. If you rename/refactor the variable and accidentally reference the "old" variable name, you'll get no error message from PowerShell. So be aware that removing variable references from your script doesn't remove it from scope.

Comments

Post comment