Controlling the Scope of Variables

Controlling the Scope of Variables

Rate This
  • Comments 12

<Wizard Warning – this topic is for wizards. Non-wizards may get confused or hurt themselves.>

PowerShell is …. well … powerful. I don't think most people have picked up on exactly how powerful it really is. There is nothing wrong with that, we are all about solving problems so if you can get your problem solved using simple techniques – that is the best of all worlds. That said, there are a number of sophisticated applications that are enabled when you explore the power of the tool. Quest's PowerGui and Full Armor's PowerShell enabled Workflow tool are two great examples of where someone used the power of the system to do some mind-bendingly wonderful stuff.

So with all that, let's talk about variables and scopes.

When you create a variable, it is available in that scope and any child scopes. The example below shows how you can define a variable in one scope and access it in a child scope (by calling a function t1) and how it is available in all child scopes (by calling a function t2 which then calls a t1):

PS> function t1 {"X = $x"}
PS> t1
X =
PS> $x="test"
PS> $x
test
PS> t1
X = test
PS> function t2 { t1 }
PS> t2
X = test
PS>

What is going on here is that each function has a "scope" (stack frame) where variables, aliases, drives, and functions get set. When you access one of these, we look for it in the current scope, if it is there, we use it, if it is not there, we look at the parent's scope (and upwards until we find the entity or reach to top).

BTW – take a minute to let that soak in. It is not just variables that get set in the scope – we do the same thing for aliases, drives, and functions. Here is an example to illustrate that:

PS> Set-Alias Run-Test Get-Date
PS> Run-Test

Saturday, April 14, 2007 12:16:10 PM


PS> function t1 { Run-Test}
PS> function t2 { Set-Alias Run-Test Get-Location; t1 }
PS> t2

Path
----
C:\ps


PS> #Notice that Run-Test got OVERRIDDEN vs OVERWRITTEN!
PS> Run-Test

Saturday, April 14, 2007 12:17:51 PM


PS>

This is soooo powerful.

You usually use the syntax above to set variables but this is really just a bit a syntactic sugar for the Set-Variable Cmdlet. The following statements are all equivalent:

PS> $x=5
PS> $x
5
PS> Set-Variable -Name x -Value 5
PS> $x
5
PS> Set-Variable -Name x -Value 5 -Scope 0
PS> $x
5
PS>

Notice the last parameter of the last Set-Variable command (-SCOPE). Let's explore that a little:

PS> Get-Help Set-Variable -Param Scope

-scope <string>
Specifies the scope from which aliases should be exported. This can be a na
med scope: "global", "local", or "script", or it can be a number relative t
o the current scope (0 through the number of scopes where 0 is the current
scope and 1 is its parent).

Required? false
Position? named
Default value local
Accept pipeline input? false
Accept wildcard characters? false

BTW –Did you realize that you can get help on individual parameters? Is that cool or what? 10,000 thanks to the superstars at Exchange that forced us to prioritize doing a good job on Help.

So now let me show you a couple of examples that you just need to look at, re-read the help above and soak in what is going on:

PS> $x="Parent"
PS> function t1 {$x}
PS> t1
Parent
PS> # Now we have the function set the variable before reporting it.
PS> # You'll see that it OVERRIDES the parent.
PS> function t1 {Set-Variable -Name x -Value "Child" -Scope 0; $x}
PS> t1
Child
PS> $x
Parent
PS>
PS> # Now we'll change the value of SCOPE to 1
PS> # You'll see that it now OVERWRITES the parent!
PS> function t1 {Set-Variable -Name x -Value "Child" -Scope 1; $x}
PS> t1
Child
PS> #Now watch what happed to $x in the parent scope!
PS> $x
Child

Now be aware, if you provide a "2" to –SCOPE, you'll set the variable in the grandparent scope and so on.

This is a very powerful technique for metaprogramming scenarios but you can get yourself in trouble if you are not careful.

Cheers!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Leave a Comment
  • Please add 3 and 7 and type the answer here:
  • Post
  • Maybe we should start our powershell software development a bit easier :) The Private statement in VBScript is concerned with classes; we’re going to take the liberty of extending the definition of Private here, primarily because we aren’t going to talk about classes. Instead of talking about private variables in terms of classes, we’re going to talk about private variables in terms of scope.

    In Windows PowerShell all you have to do is set the scope to Private. For example, this command creates a variable named $a with the scope set to private. Note that, when specifying a scope, you do not preface the variable name with a $; however, in your commands you will still refer to this variable as $a.

    Here’s the command:

    $Private:a = 5

    Because this is a private variable it will be available only in the scope in which it was created. If you type $a from the command prompt you’ll get back the value of $a. However, if you run a script that simply echoes back the value of $a you won’t get back anything; that’s because, as a private variable, $a is not available outside the command shell scope.

  • Dear PS,

       Do you have an email address?

  • First let me say that PowerShell has REALLY helped me and my company.

    We have used the tool to do some very interesting things!

    Kudos to the entire PSH development team.

    But the scoping rules described above are 'unexpected'.

    >"This is soooo powerful."

    Yes, it is powerful, but it is also error-prone.  Along with

    "GOTO considered harmful" we have over the years learned that

    global variables are harmful if not used carefully and sparingly.

    Being able to control the scope of variables and avoid unintended

    access to variables is something that experienced programmers

    have come to rely upon.

    It could be that my concern is due to my use of PowerShell in a

    manner that is not entirely as the authors intended.

    I'm using it as a scripting language (on steroids!) and not so

    much as a "shell".   Used as a shell, the above scoping concepts

    are surely powerful and helpful.  But when my scripts get to be

    a couple hundred lines of code, I begin to have reservations

    about the PowerShell scoping rules.  

    Yes, the scoping rules allow some very clever tasks to be accomplished.

    I suppose 'with power comes responsibility' and we just need

    to be diligent in our use of scoping prefixes ($global:foo).

  • Jeffrey wrote a great post for wizards about the scope of variables in PowerShell, so to maintain balance,

  • Starting to get the hang of PowerShell. Today I wanted to quickly count the number of lines in some source

  • First off, excellent article.  It's exactly the answer to what I was wondering about scoping in PowerShell.

    Clift, if you're still following this, what is your concern, specifically?  All variables are local in scope by default, so it's not like all variables are being made global, and anyone who is familiar with a scripting language should be familiar with the concept of having variables inherit down the scope.  I don't think anyone will be accidentally setting up-scoped variables, given the default syntax, so am I missing something (entirely possible)?

  • Have only been working with PowerShell a short, short time, but I've found it incredibly useful.

    How can we control the scope of functions?  For instance, lets say I have this file:

    SomeUsefulFunction.ps1

    --------------

    function SomeUsefulFunction { ... }

    Now, I want to use the function, so I try this:

    PS> ./SomeUsefulFunction.ps1

    PS> SomeUsefulFunction "parm1"

    The term 'SomeUsefulFunction' is not recognized...

    Taking a cue from this post, I tried this:

    function 1:SomeUsefulFunction { ... }

    ...but that didn't work.

    Perhaps, it would be best to address the underlying issue?  What are some of the "best practices" to organizing PS code?  Should we even be writing functions, or should we structure things like this:

    SomeUsefulFunction.ps1

    --------------

    params ( ... )

    process { ... }

    end { ... }

    PS> ./SomeUsefulFunction.ps1 "parm1"

  • Doug: Your include file idea is fine, but you have to be sure to execute it in the current process with the "." command.I.e., ".   .\Useful.ps1"

    PS Team: What the posting leaves unanswered is what creates a new scope. Is it just functions? One might suspect that each ScriptBlock would create a new scope, but that is not the case (at least in general).

  • I have imported a .Net dll in my powershell script and after importing that dll through import-module i m accessing its variable .Variable value  is alive during that particular function execution and as the function completes the variable value disappears.

    I want to access the value of that particular variable outside the function but it is always null...........i have tried a lot but  it is giving that ....................the handle is not valid.............error.

    have u guys any solution of the problem.

  • Nice artical for describe a topics. for more info check here opensourceprogrammer.in/.../variable

  • first line deserved 5 stars already ;)

Page 1 of 1 (12 items)