Command line polution – persistent variables

PowerShell variables have scope.  Scope means where the variable exists in memory and who can have access to the variables. Variables can have global, local, private or script and this is why you can have the same variable name used in multiple functions as PowerShell keeps track of the variables scope as the script runs. This is one reason why, except at the command line, I prefer not to use the in process object $_ as this can change and can get confusing in nested loops.  Sadly it’s often more efficient to use the pipeline and then of course you have to use the in process object.

If you want more details on variable scope try reading this post :  http://blogs.msdn.com/b/powershell/archive/2007/04/14/controlling-the-scope-of-variables.aspx

Normally when you run a PowerShell script the variables you create are destroyed when the script finishes. The same applies when the script returns from a function, any variables created inside the function are recovered and are not available outside of the function when back in the main part of the script – unless of course you explicitly change the scope of the variable when you create it using New-Variable and the -scope switch.

If you dot source a script then the variables become available at the command line.  This can be really useful, especially when debugging a script.  Because even after the script has run you can access the variables and manipulate them.  You can even call the functions that were part of the PowerShell script.

This isn’t always a benefit though.    Sometimes the variables that get left in memory can trip you up, especially when working within an ISE.  I prefer then, to make sure things are cleaned up and I use a little function to do this.

The meat of this code is Get-Variable which returns all the variables currently in scope and then I pipe this into a Where-Object script block that calls Remove-Variable -name $($_.name) -Force -Scope Global
this will clear out all the variables in memory.

If that were all there was to it then my post would be finished, but what if I wanted some variables to be persistent.  I can get around variable pollution by always instantiating the variables in my script and setting them to known values or clearing them by setting them to $null or an empty string in my script header and it’s good practice to do this too.

When I start my script I instantiate any variables I want to remain after the script has run then I call my clean-memory function and this looks for a Global array variable called $Global:startupVariables.  If it exists then it will remove any variables that are not in the array by doing a simple comparison -notcontains $_.name .  If the variable doesn’t exist, as would be the case at the start of my script run then it creates the variable with global scope using New-Variable -name startupVariables -Force -Scope “Global” -value ( Get-Variable | ForEach-Object { $_.Name } which is the same trick I used to clear all the variables using Get-Variable to list the existing variables.

Here’s the full code – just put it at the top of your script, instantiate any variables you want to keep and then call the function.  Then right at the end of your script call clean-memory again to clear all the variables from memory.

function Clean-Memory {
<# .SYNOPSIS Removes all variables from memory that did not exist before this script was run. .DESCRIPTION Removes all variables from memory that did not exist before this script was run.                 The script uses a global variable to record any existing variables before the   script is run and uses this to idetnify new variables which must have been   created during the script run.                 $Global:startupVariables          Call the function at the beginning of a script and then call it at the end   to clear all the veriables created during the script run.                 The script uses the Remove-Variable cmdlet to force the deletion of the variables not stored in the $Global:startupVariables variable.                 The script does not have any input parameters .EXAMPLE Clean-Memory .INPUTS None .OUTPUTS A global variable $Global:startupVariables .NOTES NAME     : Clean-Memory VERSION  : Version 1.0 AUTHOR   : Lee Andrews CREATED  : 13th November 2012 LASTEDIT : 13th November 2012 - Version 1.0 .LINK Remove-Variable .LINK   http://collaborate/technology/eng/global/platform/ps/default.aspx        #>
 if ($Global:startupvariables) {
   # if the startup variable exists we assume the call is to clean up after a script run
    Get-Variable |
     Where-Object { $Global:startupVariables -notcontains $_.Name } |
      ForEach-Object {
       try { Remove-Variable -Name "$($_.Name)" -Force -Scope "global" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue}
       catch { }
      } # remove the variables from memory that are not in the startupVariable global variable
    # now clean the startupVariables
    try {Remove-Variable -Name startupvariables  -Scope "Global" -Force -ErrorAction SilentlyContinue }
    catch { }
    # just in case this is an inital run after the script had failed in the last run lets set up the variable again
    New-Variable -name startupVariables -Force -Scope "Global" -value ( Get-Variable | ForEach-Object { $_.Name } )
  }  # If the global variable startupVariables exists then remove any variables that are not in it.
  else {
    New-Variable -name startupVariables -Force -Scope "Global" -value ( Get-Variable | ForEach-Object { $_.Name } )
  }                           # Else - Store all the start up variables in startupVariables so you can clean up when the script finishes.
} # Removes all redundant variables from memory
#

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.