[ACCEPTED]-Variable scoping in PowerShell-scope

Accepted answer
Score: 91

The PowerShell scopes article (about_Scopes) is nice, but 7 too verbose, so this is quotation from my 6 article:

In general, PowerShell scopes are like 5 .NET scopes. They are:

  • Global is public
  • Script is internal
  • Private is private
  • Local is current stack level
  • Numbered scopes are from 0..N where each step is up to stack level (and 0 is Local)

Here is simple example, which 4 describes usage and effects of scopes:

$test = 'Global Scope'
Function Foo {
    $test = 'Function Scope'
    Write-Host $Global:test                                  # Global Scope
    Write-Host $Local:test                                   # Function Scope
    Write-Host $test                                         # Function Scope
    Write-Host (Get-Variable -Name test -ValueOnly -Scope 0) # Function Scope
    Write-Host (Get-Variable -Name test -ValueOnly -Scope 1) # Global Scope
}
Foo

As 3 you can see, you can use $Global:test like 2 syntax only with named scopes, $0:test will 1 be always $null.

Score: 76

You can use scope modifiers or the *-Variable cmdlets.

The scope modifiers 4 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 as global.

(For the -Scope parameter of the *-Variable cmdlets 3 see the help.)

Eg. in your second example, to 2 directly modify the global $array:

& {
  $global:array +="s"
  Write-Host $array
}

For more details 1 see the help topic about_scopes.

Score: 17

Not just varibles. When this says "item" it 17 means variables, functions, aliases, and 16 psdrives. All of those have scope.

LONG DESCRIPTION  
    Windows PowerShell protects access to variables, aliases, functions, and
    Windows PowerShell drives (PSDrives) by limiting where they can be read and
    changed. By enforcing a few simple rules for scope, Windows PowerShell
    helps to ensure that you do not inadvertently change an item that should
    not be changed.

    The following are the basic rules of scope:

        - An item you include in a scope is visible in the scope in which it
          was created and in any child scope, unless you explicitly make it
          private. You can place variables, aliases, functions, or Windows
          PowerShell drives in one or more scopes.

        - An item that you created within a scope can be changed only in the
          scope in which it was created, unless you explicitly specify a
          different scope.

The copy 15 on write issue you're seeing is because 14 of the way Powershell handles arrays. Adding 13 to that array actually destroys the original 12 array and creates a new one. Since it was 11 created in that scope, it is destroyed when 10 the function or script block exits and the 9 scope is disposed of.

You can explicitly 8 scope varibles when you update them, or 7 you can use [ref] objects to do your updates, or 6 write your script so that you're updating 5 a property of an object or a hash table 4 key of an object or hash table in a parent 3 scope. This does not create a new object 2 in the local scope, it modifies the object 1 in the parent scope.

Score: 5

While other posts give lots of useful information 24 they seem only to save you from RTFM.
The 23 answer not mentioned is the one I find most 22 useful!

([ref]$var).value = 'x'

This modifies the value of $var no 21 matter what scope it happens to be in. You 20 need not know its scope; only that it does 19 in fact already exist. To use the OP's 18 example:

$array=@("g")
function foo()
{
    ([ref]$array).Value += "h"
    Write-Host $array
}
& {
    ([ref]$array).Value +="s"
    Write-Host $array
}
foo
Write-Host $array

Produces:

g s
g s h
g s h

Explanation:
([ref]$var) gets you 17 a pointer to the variable. Since this is 16 a read operation it resolves to the most 15 recent scope that actually did create that 14 name. It also explains the error if the 13 variable doesn't exist because [ref] can't 12 create anything, it can only return a reference 11 to something that already exists.

.value then takes 10 you to the property holding the variable's 9 definition; which you can then set.

You may 8 be tempted to do something like this because 7 it sometimes looks like it works.

([ref]$var) = "New Value"

DON'T!!!!
The 6 instances where it looks like it works is 5 an illusion because PowerShell is doing 4 something that it only does under some very 3 narrow circumstances such as on the command 2 line. You can't count on it. In fact it 1 doesn't work in the OP example.

More Related questions