A sad thing about PowerShell is that function and scriptblocks are dynamically scoped.
But there is another thing that surprised me is that variables behave as a copy-on-write within an inner scope.
$array=@("g")
function foo()
{
$array += "h"
Write-Host $array
}
& {
$array +="s"
Write-Host $array
}
foo
Write-Host $array
The output is:
g s
g h
g
Which makes dynamic scoping a little bit less painful. But how do I avoid the copy-on-write?
You can use scope modifiers or the
*-Variable
cmdlets.The scope modifiers are:
global
used to access/modify at the outermost scope (eg. the interactive shell)script
used on access/modify at the scope of the running script (.ps1
file). If not running a script then operates asglobal
.(For the
-Scope
parameter of the*-Variable
cmdlets see the help.)Eg. in your second example, to directly modify the global
$array
:For more details see the help topic about_scopes.
Not just varibles. When this says "item" it means variables, functions, aliases, and psdrives. All of those have scope.
The copy on write issue you're seeing is because of the way Powershell handles arrays. Adding to that array actually destroys the original array and creates a new one. Since it was created in that scope, it is destroyed when the function or script block exits and the scope is disposed of.
You can explicitly scope varibles when you update them, or you can use [ref] objects to do your updates, or write your script so that you're updating a property of an object or a hash table key of an object or hash table in a parent scope. This does not create a new object in the local scope, it modifies the object in the parent scope.
The PowerShell scopes article (about_Scopes) is nice, but too verbose, so this is quotation from my article:
In general, PowerShell scopes are like .NET scopes. They are:
Here is simple example, which describes usage and effects of scopes:
As you can see, you can use $Global:test like syntax only with named scopes, $0:test will be always $null.