I want to remove all user-created variables at the start of a script.
Currently I am doing Remove-Variable -Name *
but it tries to delete all system variables as well, resulting in lot of error messages.
Any other way?
I want to remove all user-created variables at the start of a script.
Currently I am doing Remove-Variable -Name *
but it tries to delete all system variables as well, resulting in lot of error messages.
Any other way?
Since all the system variables are read-only or constant anyway you can just silence the errors:
Remove-Variable * -ErrorAction SilentlyContinue
But admittedly, you should probably exclude a few anyway:
Get-Variable -Exclude PWD,*Preference | Remove-Variable -EA 0
Invoke a new instance of PowerShell, get the built-in variables, then remove everything else that doesn't belong.
$ps = [PowerShell]::Create()
$ps.AddScript('Get-Variable | Select-Object -ExpandProperty Name') | Out-Null
$builtIn = $ps.Invoke()
$ps.Dispose()
$builtIn += "profile","psISE","psUnsupportedConsoleApplications" # keep some ISE-specific stuff
Remove-Variable (Get-Variable | Select-Object -ExpandProperty Name | Where-Object {$builtIn -NotContains $_})
Instead of deleting all the user variables, start a fresh instance of PowerShell:
PS C:\> $x = 10
PS C:\> $y = 50
PS C:\> $blah = 'text'
PS C:\> Write-host $x $y $blah
10 50 text
PS C:\> powershell
Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.
PS C:\> Write-host $x $y $blah
PS C:\>
User defined variables won't carry over into the new instance.
PS C:\> $bleh = 'blue'
PS C:\> Write-Host $bleh
blue
PS C:\> exit
PS C:\> Write-host $bleh
PS C:\>
Your variables won't carry back over into the calling instance, either.
You have a few options in terms of how to actually accomplish this.
You can always start the new instance yourself, of course:
powershell -ExecutionPolicy Unrestricted -File myscript
You should not rely on those variables. Anything that was not declared is just not used. If you need to pass things around, use parameters.
If you keep up to this principle, you would just overwrite your user-created variable and use it like it never existed.
This is enterprise best practices that let you infinitely scale your scripts. Not just in Powershell, but in any language which could have global variables, such as Javascript.
Here is a hack that will delete all variables except those created in your $profile. Note that this takes a few seconds to run, and there's a pretty good chance you could achieve your end goal in a more efficient way. You'll have to adjust the "15" in the select statement to correspond to the number of lines your profile script spits out. Or you could modify it to search for the first line from gv (get-variable).
$x=powershell -nologo -command "gv|out-string"
$y=($x|select -skip 15) -split "\s+",2
$varnames = $(for ($i=0;$i -lt $y.length; $i+=2) { $y[$i]})
rv i,x,y
gv | % {if ($varnames -notcontains $_.name) {rv $_.name -EA 0}}
Caveat: this won't delete variables created by Powershell hosts that specify an InitialSessionState that loads modules or executes scripts that create variables. However for normal Powershell and powershell_ise you don't have to worry about that.
Using Joey's answer works well... perhaps too well. When I used within Visual Studio Code, for instance, its $psEditor
variable was removed and its integrated terminal started throwing errors.
To be clear, I was using Get-Variable -Exclude PWD,*Preference | Remove-Variable -EA 0
as part of my script before firing off the rest of the script to ensure it was in a clean situation, but it over-cleaned.
I got around this issue by adding $psEditor
to the exceptions list like this:
Get-Variable -Exclude PWD,*Preference,psEditor | Remove-Variable -EA 0
But that's kind of a whack-a-mole approach, and there seem to be better ways, depending on how much control you have over the scripts in question.
For instance, here's a nice solution from SeeminglyScience from that issue I raised in GitHub, though it essentially reduces to the logic in ferrell_io's answer with a few important improvements:
$existingVariables = Get-Variable
try {
# your script here
} finally {
Get-Variable |
Where-Object Name -notin $existingVariables.Name |
Remove-Variable
}
That says...
That's a nice "leave it no worse than you found it" workaround.
Maybe worth mentioning that the VS Code integrated terminal leaving important variables around in scope isn't perfect, and some responses to the issue raised said they might change $psEditor
to a constant of some sort so that Remove-Variable
couldn't do something this accidentally destructive.
I would pull the list of existing variable names into an array at the beginning of the script, then remove any newly added variables at the end of the script:
### Start of script (store list of existing variable names)
$ExistingVariables = Get-Variable | Select-Object -ExpandProperty Name
<#
Script contents go here
#>
### End of script (remove new variables)
$NewVariables = Get-Variable | Select-Object -ExpandProperty Name | Where-Object {$ExistingVariables -notcontains $_ -and $_ -ne "ExistingVariables"}
if ($NewVariables)
{
Write-Host "Removing the following variables:`n`n$NewVariables"
Remove-Variable $NewVariables
}
else
{
Write-Host "No new variables to remove!"
}
You can also consider changing the scope of you variables. Variables contained within a loop or function for example only exist inside that scope. As long as the variables are not created within the global or script scope, they won't persist after the script finishes. You can then just remove the global / script scope variables. https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7.1
I suppose you could make a list of all the variables you don't want to delete.
$DontDelete = '$','?','^','args',...,'WhatIfPreference'
Then delete the variables that are not on the Don't Delete list.
You can't. You'll have to keep track of the available variables before the script started and remove any variables added after the script executed.