Hey, Scripting Guy! A Scripting Overview of Windows PowerShell 2.0 (Part 2)

Hey, Scripting Guy! A Scripting Overview of Windows PowerShell 2.0 (Part 2)

  • Comments 4
  • Likes

Bookmark and Share

(Note: Today's Hey, Scripting Guy! Blog post is an edited excerpt from a forthcoming SAPIEN Press book authored by Don Jones and Jeffery Hicks. Don has published more than 30 books about IT pro topics; you can also find him in TechNet Magazine, on ScriptingAnswers.com, and on concentratedtech.com. Jeffery has authored or co-authored several books in the SAPIEN TFM series; he is a Windows PowerShell MVP, a columnist and contributing editor for Redmondmag.com, and a columnist for MCPMag.com. Find out more about Jeffery at http://jdhitsolutions.com/blog.

Today is part 2 of 2. Part 1 was published yesterday. Thanks again, Don and Jeffery!)

----------

Scripting Basics

Keep in mind that everything we’re covering related to scripting works fine when you’re using the shell interactively. So, you can use everything we’re about to show you even if you’re not planning on writing scripts at all.

Scope

Now that you’re going to begin working with scripts, you’re going to run up against a concept called scope, which is very important in Windows PowerShell. So far, we’ve just been working interactively in the shell, which is referred to as the global scope. When you’re just working interactively in the shell, everything occurs in the global scope, so it’s like there’s no scope at all.

However, when you run a script, Windows PowerShell creates a new script scope, which contains the script. The script scope is a child of the global scope; the global scope is referred to as the script scope’s parent. Some special rules govern interaction between the two scopes:

·         The parent scope cannot see “inside” the child scope.

·         The child scope can read elements of the parent scope but can modify them only if a special syntax is used.

·         If a child scope attempts to modify a parent scope element without using the special syntax, a new element of the same name is created within the child scope, and the child scope effectively “loses” access to the parent scope element of that name.

Elements, in the above rules, refer primarily to variables and functions. To reiterate these rules in a variable-centric sense:

·         The parent scope cannot access variables, which are defined in a child scope.

·         The child scope can read variables defined in the parent scope but can modify them only if a special syntax is used.

·         If a child scope attempts to modify a parent scope variable without using the special syntax, a new variable of the same name is created within the child scope, and the child scope effectively “loses” access to the parent scope variable of that name.

When you create a function—either by defining it in the global scope or, more commonly, within a script—the function itself is a local scope, and is considered a child of whatever scope it was created in. Here’s a quick example—we haven’t discussed functions yet, but this one isn’t complicated, so we hope it’ll help illustrate this scope stuff:

1.  $var1 = "Hello"

2.  Function MyFunction {Write-Host $var1

3.   $var1 = "Goodbye"

4.   Write-Host $var1

5.  }

6.  Write-Host $var1

7.  MyFunction

8.  Write-Host $var1


If you were to run this script, here’s the output you’d see:

Hello

Hello

Goodbye

Hello


Why is this true? The first executable line in this script is the first line, which sets the variable $var1 equal to the value “Hello”. Next, a function is defined, but not yet executed. Windows PowerShell skips over the function definition to line 7, where the contents of $var1 are displayed—our first line of output. Next, line 8 calls MyFunction. This enters the function, which is a child scope of the script scope. Line 3 displays the contents of $var1. Because $var1 hasn’t been defined in this scope, Windows PowerShell looks to the parent scope to see if $var1 exists there. It does, and so our second line of output is also “Hello”. Line 4 assigns a new value to $var1. Because a scope cannot directly modify its parent’s variables; however, line 4 is actually creating a new variable called $var1. When line 5 runs, $var1 now exists in the local scope, so our third line of output is “Goodbye”. When we exit the function, its scope is discarded. When line 9 runs, $var1 still contains its original value—the function never modified this $var1—so our last line of output is “Hello” again.

Now take a look at this slight revision:

1.  $var1 = "Hello"

2.  Function MyFunction {

3.   Write-Host $var1

4.   $script:var1 = "Goodbye"

5.   Write-Host $var1

6.  }

7.  Write-Host $var1

8.  MyFunction

9.  Write-Host $var1


We made the one line we changed (line 4) bold. This time, you’ll see:

Hello

Hello

Goodbye

Goodbye


Inside the function we’ve used the special syntax that allows a child scope to explicitly modify its parent’s variables. When we first look at $var, it has a value of “Hello”. Then we call the function, which reads $var from the parent scope. Then we make our special change using a scope identifier. There are four of these scope identifiers that Windows PowerShell recognizes:

·         $global: works with objects in the global scope.

·         $script: works with objects in the parent script scope.

·         $local: works with objects in the local scope.

·         $private: works with objects in a private scope.

When we attempt to read $var again, it is now set to “Goodbye”. When the script ends, the parent scope has been modified, and $var still equals “Goodbye”.

There’s another technique, called dot sourcing, which impacts scope. Take a look at our original example script, this time with additional modifications:

$var1 = "Hello"

Function MyFunction {

 Write-Host $var1

 $var1 = "Goodbye"

$var2 = "I'm new here"Write-Host $var1

}

Write-Host $var1

Write-Host $var2

. MyFunction

Write-Host $var1

Write-Host $var2

Notice how we’re calling the function on the second-to-last line of code? We’ve typed a period, followed by a space, and then the function name. This is called dot sourcing, and it forces the function to run, not in its own scope but rather right within the script scope. In other words, when you run something—a script or a function—using dot sourcing, you tell Windows PowerShell to forgo the step of creating a new scope and to instead run all the commands in the current scope. In this revised example, you should see output like this:

Hello

 

Hello

Goodbye

Goodbye

I'm new here


The first “Hello” is the value of $var1 before the function is called. The script then tries to display $var2, which is undefined. In the dot sourced function, $var1 is displayed again. It is changed to “Goodbye” and displayed. After the function, $var1 has now been changed and because $var2 was defined in the dot sourced function, it now exists in the main script scope.

Dot sourcing is a useful trick. For example, you could write a script that does nothing but define a bunch of utility functions—that is, functions that do some useful tasks that you use from time to time (almost like cmdlets). By dot sourcing that script into the global scope, those functions become defined within the global scope, making them available to you just like a cmdlet or global variable.

Scope in Windows PowerShell is actually a lot more expansive than we’ve covered here. Our goal here was to introduce you to the concept, because it will impact most of your scripts.

 

That concludes part 2 of 2. See you tomorrow for another installment of Quick-Hits Friday!

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • A very good article.  However, unless I am mistaken that last script is not going to work.  Two issues:

    1) The line "$var2 = "I'm new here"Write-Host $var1" should be split to two lines as below:

    $var2 = "I'm new here"

    Write-Host $var1

    2) The output "I'm new here" will not come from that script as $var2 is never called from a write-host command after it is defined.  The actual output of that script is an error.  If you fix the issue listed in 1) above then the output looks like this:

    Hello

    Hello

    Goodbye

    Goodbye

    To get the output explained in the artcle you would have to add one more line at the end of the script:

    Write-Host $var2

    Thanks!

  • There are code formatting problems on the last example as you found. The write-host command for var2 got cutoff which is why the output doesn't match.

  • I have gone in and edited the code to read correctly. Thanks for pointing this out Troy and thanks Jeffery for writing the blog and sharing your knowledge, plus providing the fix.

  • A dictionary variable defined in a parent scope can be modified (new keys added, removed, or values changed) in a child scope without using the parent scope prefix. However, if you need to redefine it using the "=" sign, you'll have to use the parent scope prefix.

    An array variable defined in a parent scope can have it's existing members changed in a child scope without using the parent scope prefix. However, if you need to add a new item to it using the "+=" sign, or redefine it using the "=" sign, you'll have to use the parent scope prefix.