2

When running PowerShell scripts at the console, scoping helps keep script variables from bleeding into the global environment. However, this seems to not quite apply in the PowerShell ISE. Thus, I've been trying to come up with an easy method to track variables in a script so they can be cleaned up whenever the script exits. A test script I've written to demonstrate this is below:

# Start from clean screen.
Clear-Host

# Set initial variables.
$Var1 = 1
$Var2 = 2
$Var3 = 3

# Initialize variables list.
$VarsList = New-Object System.Collections.ArrayList

# Function to add a variable.
function NewVar
{
    $VarsList.Add($Args) | Out-Null
}

# Add initial variables to $VarsList.
'Var1','Var2','Var3' | ForEach-Object {NewVar $_}

# Check VarsList
Write-Host "VarsList:"
$VarsList
Write-Host ""

# Add a new variable to test NewVar.
$Var4 = 4
NewVar Var4

# Check VarsList
Write-Host "VarsList after Var4:"
$VarsList
Write-Host ""

# Function to remove variable from environment and variables list.
function KillVar
{
    Remove-Variable $Args -Scope 1
    $VarsList.Remove($Args[0])
}

# Function to remove all variables.
function VarGenocide
{
    $VarsList | ForEach-Object {Remove-Variable $_ -Scope 1}
    Remove-Variable VarsList -Scope 1
}

# Try to use KillVar
KillVar Var3

# Check Variables
Write-Host "Variables after KillVar Var3:"
Write-Host "Var1 $Var1"
Write-Host "Var2 $Var2"
Write-Host "Var3 $Var3"
Write-Host "Var4 $Var4"
Write-Host ""

# Check $VarsList
Write-Host "VarsList after KillVar Var3:"
$VarsList
Write-Host ""

# Try to remove all variables
VarGenocide

# Check Variables
Write-Host "Variables after VarGenocide:"
Write-Host "Var1 $Var1"
Write-Host "Var2 $Var2"
Write-Host "Var3 $Var3"
Write-Host "Var4 $Var4"
Write-Host ""

# Check $VarsList
Write-Host "VarsList after VarGenocide:"
$VarsList

Getting the variables removed doesn't seem to be a problem. However, updating $VarsList for removed variables, appears to be more difficult than I'd anticipated. Here's the output of the above script.

enter image description here

As you can see, 'Var3' was not removed from $VarsList when I used KillVar. This resulted in the "Cannot find a variable" error when VarGenocide was attempted.

I have a feeling this has something to do with variable scoping. However, I'm not sure how to properly handle it since I'm not using PowerShell's built-in array class (see here for why). Am I right, and is there a way to work around this? Or is there another problem I'm not seeing?

I'm using PowerShell 4.0 on Windows 8.1, though I'm pretty sure this should be valid down to 2.0 on XP.

Community
  • 1
  • 1
Iszi
  • 777
  • 3
  • 12
  • 26

1 Answers1

2

The function NewVar has a subtle mistake. $Args is an array, so that $VarsList.Add($Args) does not add strings (variable names), it adds arrays. Later you search for a string Var3 there and it is not found, correctly.

One way to fix this is to use AddRange instead of Add:

# Function to add a variable.
function NewVar
{
    $VarsList.AddRange($Args)
}

Note that you can omit | Out-Null because AddRange is void. BTW, the most effective way to discard output is $null = ....

Roman Kuzmin
  • 40,627
  • 11
  • 95
  • 117
  • Aside from being longer, why do you say Out-Null is less effective? Also, might there be a way I could have caught the array problem earlier, e.g. by formatting the output of $VarsList differently? – Iszi Jun 22 '14 at 16:58
  • 1. `Out-Null` - see http://stackoverflow.com/a/5263780/323582 2. Probably but this way would require some coding to avoid so called "unrolling" on output. – Roman Kuzmin Jun 22 '14 at 17:34
  • Now that you've helped me understand the problem, it seems changing `$Args` in `NewVar` to `$Args[0]` was a simpler solution. I'm guessing using `AddRange` has an added advantage of being usable for multiple variables without the need for piping to `ForEach-Object`, but I'm curious to know if there might also have been another reason you recommended that instead? – Iszi Jun 22 '14 at 19:42
  • No, I have just provided the solution that fits the original approach. – Roman Kuzmin Jun 22 '14 at 20:09