Author Posts

October 19, 2015 at 6:36 am

Looking through the brilliant "PowerShell in Action" and came across a script in the book that i need some clarification on.

Function New-counter ($increment=1)
{ $count=0;
    {
    $script:count += $increment
    $count
}.GetNewClosure()
}

I'm trying to get my head round this part $script:count += $increment.

The $script is a new variable but it seems to be attaching the $count variable...$script:count...
then passed to a new variable from the function....$increment

How is this working ?

October 19, 2015 at 6:39 am

This is variable scoping. It changes the variable scope to script rather than it's normal local scope inside of a function. Another one you will see occasionally is $global:variable. This changes the scope of the variable to global. Check out about_Scopes for more info.

https://technet.microsoft.com/en-us/library/hh847849.aspx

October 19, 2015 at 7:04 am

This particular example is a bit tricker than that. It's demonstrating the ScriptBlock.GetNewClosure() behavior. Because you've called GetNewClosure(), the script: scope modifier for each of the script block returned by New-Counter is _not_ actually in your script's script: scope. Here's an example of what I mean:

Function New-counter ($increment=1)
{ $count=0;
    {
    $script:count += $increment
    $count
}.GetNewClosure()
}

$script:count = 14

$ctr1 = New-counter -increment 1
$ctr2 = New-counter -increment 3

& $ctr1
& $ctr1
& $ctr2
& $ctr2

Notice that I set $script:count to 14 before calling New-Counter. However, the output from this command is:

1
2
3
6

Each counter maintains its own increment and count, isolated from the other counters (and from the actual script itself.) This is a pretty advanced technique for most PowerShell scripters, and can easily become pretty confusing to keep track of what's going on.

October 19, 2015 at 8:23 am

Nice explanation, thanks Dave.

October 19, 2015 at 12:53 pm

Thank you Curtis and Dave.
Your right, not easy to get your head round.

Could you call the :variable static? So you want to use it as a base but keep the original variable as is?

October 19, 2015 at 2:36 pm

Graham, it sounds like you are talking about a constant or readonly variable. Check out the help on Set-Variable.

Set-Variable test -option Constant -value 100
Set-Variable test -option ReadOnly -value 100

October 19, 2015 at 9:57 pm

Thanks Curtis, but in trying to get my head around the scopes, I'm not looking for s solution, just an understanding. I was trying in my simple terms to learn how scopes work.

October 20, 2015 at 1:00 am

Here's a good test to give you an idea of the differences between Global, Script and Local (read: only within a function)

Create a new ps1 script with the following content:

$Script:var = "I AM SCRIPT"

function test {
    $var = "I AM FUNCTION"
    "Testing `$Global:var = $Global:var"
    "Testing `$script:var = $Script:var"
    "Testing `$var = $var"
}

test
$var

And save as something like C:\test.ps1. Then open a powershell console and run the following:

$var = "I AM GLOBAL"
&C:\test.ps1

Your output should be:
Testing $Global:var = I AM GLOBAL
Testing $script:var = I AM SCRIPT
Testing $var = I AM FUNCTION
I AM SCRIPT

This is a very simple example, but I think you should get the gist of it.

A practical example: If for instance you wish to set $ErrorActionPreference within a script or function without modifying the $ErrorActionPreference of your console session. You can modify the value within any script or function, run that script or function in a console, and the value will ONLY be changed within the context of the script or function that has been executed.

If you wish to change the $ErrorActionPreference of your console session by running a function or script, you can reference $Global:ErrorActionPreference in your script or function, then when that script or function is executed in your console session it will update the Global ErrorActionPreference for that console session.

Extracted from about_scopes:

– 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.

October 20, 2015 at 11:16 am

Thanks Peter, i shall have a play 🙂