270
CHAPTER 7
P
OWER
S
HELL
FUNCTIONS
care.) This significant difference is in how assignment is done. In tradi-
tional dynamic scoping, if a variable exists in an outer scope, then it will 
be assigned to the current scope. In PowerShell, even if there’s an exist-
ing variable in an outer scope, a new local variable will be created on first 
assignment. This guarantees that a function, in the absence of scope 
modifiers, won’t mess up the calling scopes (hence the term hygienic).
7.6.1
Declaring variables
Ignoring function parameters (which are a form of declaration), PowerShell has no 
variable declaration statement. In contrast to a language like Visual Basic, which uses 
Dim
to declare a variable, in PowerShell a variable simply comes into existence on first 
assignment. We discussed this in chapter 5, but it’s more important now. Figure 7.8 
shows a diagram of how variable names are resolved in PowerShell.
Let’s look at an example. First define two simple functions, 
one
and 
two
:
PS (1) > function one { "x is $x" }
PS (2) > function two { $x = 22; one }
Function 
one
prints out a string displaying the value of 
$x
. Function 
two
sets the 
variable 
$x
to a particular value, and then calls function 
one
. Now let’s try them out. 
Before you work with the functions, set 
$x
to 7 interactively, to help illustrate how 
scoping works:
PS (3) > $x=7
Now call function 
one
:
PS (4) > one 
x is 7
Function scope:
function one { "x is $x y is $y" }
returns “x is 22 y is 2”
Global scope:
$x = 7; $y = 2
Function scope:
function two{ $x = 22;  one }
c
s
s
i
t
c
y
c
p
op
o
2
User calls function two
two calls one
Figure 7.8 How variables are resolved across different scopes. They’re resolved first in 
the local scope, then in the immediate caller’s scope, and so on until the global scope is 
reached. In this case, lookup of 
$x
resolves to 22 in the scope for function 
one
. Lookup 
of 
$y
resolves to 2 in the global scope, resulting in the output string “x is 22 y is 2”.
Pdf reverse page order preview - re-order PDF pages in C#.net, ASP.NET, MVC, Ajax, WinForms, WPF
Support Customizing Page Order of PDF Document in C# Project
reorder pages in pdf file; move pages in pdf reader
Pdf reverse page order preview - VB.NET PDF Page Move Library: re-order PDF pages in vb.net, ASP.NET, MVC, Ajax, WinForms, WPF
Sort PDF Document Pages Using VB.NET Demo Code
reordering pages in pdf; how to reorder pages in pdf online
V
ARIABLE
SCOPING
IN
FUNCTIONS
271
As expected, it prints 
x is 7
. Now call function 
two
:
PS (5) > two 
x is 22
Not surprisingly, because 
two
sets 
$x
to 22 before calling 
one
, you see 
 is  22 
returned. So what happened to 
$x
? Let’s check:
PS (6) > $x 
7
It’s still 7! Now call 
one
again:
PS (7) > one 
x is 7
It prints 
x is 7
. So what exactly happened here? When you first assigned 7 to 
$x
you created a new global variable, 
$x
. When you called function 
one
the first time, it 
looked for a variable 
$x
, found the global definition, and used that to print the mes-
sage. When you called function 
two
, it defined a new local variable called 
$x
before 
calling 
one
. This variable is local—that is, it didn’t change the value of the global 
$x
but it did put a new 
$x
on the scope stack. When it called 
one
, this function searched 
up the scope stack looking for 
$x
, found the new variable created by function 
two
and used that to print 
x is 22
. On return from function 
two
, the scope containing 
its definition of 
$x
was discarded. The next time you called function 
one
, it found 
the top-level definition of 
$x
. Now let’s compare this to a language that’s lexically 
scoped. I happen to have Python installed on my computer, so from PowerShell, I’ll 
start the Python interpreter:
PS (1) > python 
Python 2.2.3 (#42, May 30 2003, 18:12:08) [MSC 32 bit (Intel)] on
win32 
Type "help", "copyright", "credits" or "license" for more informa 
tion.
Now  let’s  set the global variable 
x
to 7. (Note—even if you aren’t familiar with 
Python, these examples are very simple, so you shouldn’t have a problem following 
them.)
>>> x=7
Now print 
x
to make sure it was properly set:
>>> print x 
7
You see that it is, in fact, 7. Now let’s define a Python function 
one
:
>>> def one(): 
...     print "x is " + str(x) 
...
272
CHAPTER 7
P
OWER
S
HELL
FUNCTIONS
And now define another function 
two
that sets 
x
to 22 and then calls 
one
:
>>> def two(): 
...     x=22 
...     one() 
...
As with the PowerShell example, 
one
prints 
x is 7
>>> one() 
x is 7
Now call 
two
:
>>> two() 
x is 7
Even though 
two
defines 
x
to be 22, when it calls 
one
one
still prints 7. This is 
because the local variable 
x
isn’t lexically visible to 
one
—it will always use the value of 
the global 
x
, which you can see hasn’t changed:
>>> print x 
>>>
At this point, I hope you have a basic understanding of how variables are looked up 
in PowerShell. Sometimes, though, you want to be able to override the default lookup 
behavior. We’ll discuss this in the next section.
NOTE
Unix shells used dynamic scoping because they didn’t have a 
choice. Each script is executed in its own process and receives a copy of 
the  parent’s  environment.  Any  environment  variables  that  a  script 
defines will then be inherited by any child scripts that it, in turn, calls. 
The process-based nature of the Unix shells predetermines how scop-
ing can work. The interesting thing is that these semantics are pretty 
much what PowerShell uses, even though the PowerShell team wasn’t 
limited by the process boundary. The team tried a number of different 
schemes and the only one that was satisfactory was the one that most 
closely mimicked traditional shell semantics. I suppose this shouldn’t 
be a surprise—it’s worked well for several decades now. 
7.6.2
Using variable scope modifiers
We’ve now arrived at the subject of variable scope modifiers. In the previous section 
we discussed scope and the default PowerShell lookup algorithm. Now you’ll see that 
you can override the default lookup by using a scope modifier. These modifiers look 
like the namespace qualifiers mentioned in chapter 6. To access a  global variable 
$var
, you’d write
$global:var
S
UMMARY
273
Let’s revisit the functions from the previous section:
PS (1) > function one { "x is $global:x" }
This time, in the function 
one
, you’ll use the scope modifier to explicitly reference 
the global 
$x
:
PS (2) > function two { $x = 22; one }
The definition of function 
two
is unchanged. Now set the global 
$x
to 7 (com-
mands at the  top level  always  set global  variables, so you  don’t need  to  use the 
global modifier):
PS (3) > $x=7
Now run the functions:
PS (4) > one 
x is 7 
PS (5) > two 
x is 7
This time, because you told 
one
to bypass searching the scope change for 
$x
and go 
directly to  the  global variable, calls to  both 
one
and 
two
return the same result, 
x is 7
.
When we look at scripts in chapter 8, you’ll see that there are additional scoping 
rules and qualifiers, but for now, you have all you need to work with functions.
In the next chapter, you’ll extend your PowerShell programming knowledge to 
include writing scripts. We’ll also look at some of the advanced features in Power-
Shell, especially new features introduced with PowerShell v2 that you can use for 
your work.
7.7
S
UMMARY
This chapter introduced the idea of programming in PowerShell. We covered a lot of 
material; here are the key points:
• PowerShell programming can be done either with functions or scripts, though 
in this chapter we focused only on functions.
• Functions are created using the 
function
keyword.
• The simplest form of function uses 
$args 
to receive parameters automatically.
• More sophisticated parameter handling for functions requires the use of param-
eter declarations. This can be done by placing the parameter names in parenthe-
ses after the name of the function or in the body of the function using the 
param
keyword. 
• PowerShell uses dynamic scoping for variables. You can modify how a variable 
name is resolved by using the scope modifiers in the variable names. 
274
CHAPTER 7
P
OWER
S
HELL
FUNCTIONS
• Functions stream their output. In other words, they return the results of every 
statement executed as though it were written to the output stream. This feature 
means that you almost never have to write your own code to accumulate results. 
• Because of the differences between how functions work in PowerShell and how 
they work in more conventional languages, you may receive some unexpected 
results when creating your functions, so you picked up some tips on debugging 
these problems.
• Functions can be used as filters using the 
filter
keyword or by specifying 
begin, process, and end blocks in the function body.
• The function: drive is used to manage the functions defined in your session. 
This means that you use the same commands you use for managing files to 
manage functions. 
275
C
H
A
P T E R   8
Advanced functions 
and scripts
8.1  PowerShell scripts  276
8.2  Writing advanced functions and 
scripts  287
8.3  Dynamic parameters and dynamic-
Param  311
8.4  Documenting functions and 
scripts  314
8.5  Summary  321
And now for something completely different…
—Monty Python
In chapter 7, we introduced the basic elements needed for programming in Power-
Shell when we looked at PowerShell functions. In this chapter we’re going to expand 
our repertoire by introducing PowerShell scripts. 
NOTE
If you skipped chapter 7, you should probably go back and 
read it before proceeding. Why? Because all the material we covered on 
functions also applies to scripts.
Once we’re finished with the basics of scripts (which won’t take long), we’ll move on 
to the advanced production scripting features introduced in PowerShell v2. With 
these new features, it’s possible to use the PowerShell language to write full-featured 
applications complete with proper documentation. By the end of this chapter, you 
should be well on your way to becoming an expert PowerShell programmer.
276
CHAPTER 8
A
DVANCED
FUNCTIONS
AND
SCRIPTS
8.1
P
OWER
S
HELL
SCRIPTS
In this section we’re going to dig into scripts to see what behaviors they have in com-
mon with functions and what additional features you need to be aware of. We’ll begin 
by looking at the execution policy that controls what scripts can be run. Then you’ll 
see how parameters and the 
exit
statement work in scripts. We’ll also spend some 
time on the additional scoping rules that scripts introduce. Finally, you’ll learn ways 
you can apply and manage the scripts you write.
Let’s begin with defining what a script is. A PowerShell script is simply a file with 
a .ps1 extension that contains some PowerShell commands. Back in chapter 1, we 
talked about how PowerShell has the world’s shortest “Hello world” program. The 
full text of that script was
"Hello world"
That’s it—one line. Let’s create this script now. You can do it from the command line 
using redirection to write the script text to a file called hello.ps1:
PS (2) > '"Hello world"' > hello.ps1
Note the double quotes in the example. You want the script to contain
"Hello world"
with the quotes intact, not
Hello world
Now execute the script:
PS (3) > ./hello.ps1 
Hello world
You see that the file executed and returned the expected phrase.
NOTE
In this example, even though hello.ps1 is in the current direc-
tory, you had to put 
./
in front of it to run it. This is because Power-
Shell  doesn’t  execute  commands  out  of  the  current  directory  by 
default. This prevents accidental execution of the wrong command. 
See chapter 13 on security for more information.
8.1.1
Script execution policy
Now there’s a possibility that instead of getting the expected output, you received a 
nasty-looking error message that looked something like this:
PS (5) > ./hello.ps1 
The file C:\Documents and Settings\brucepay\hello.ps1 cannot be 
loaded. The file C:\Documents and Settings\brucepay\hello.ps1 is
not digitally signed. The script will not execute on the system 
. Please see "get-help about_signing" for more details.
At line:1 char:11 
+ ./hello.ps1 <<<<
P
OWER
S
HELL
SCRIPTS
277
This is another security feature in PowerShell. When PowerShell is first installed, by 
default you can’t run any scripts. This is controlled by a feature called the execution 
policy. The execution policy setting controls what kind of scripts can be run and is 
intended to prevent virus attacks like the “I-love-you” virus from a few years back. 
Users  were  being  tricked  into  accidentally  executing  code  mailed  to  them.  The 
default execution policy for PowerShell prevents this type of attack.
A scripting tool is no good if you can’t script, so there’s a cmdlet called 
Set-
ExecutionPolicy
that can be used to change the execution policy. If you got the 
error when you tried to execute the script, you should run the following command as 
Administrator. 
If you don’t have administrator access, there’s an alternative we’ll get to in a second. 
Here’s the command:
PS (6) > Set-ExecutionPolicy remotesigned
After the command has run successfully, you should be able to run hello.ps1:
PS (7) > ./hello.ps1 
Hello world
NOTE
Running the cmdlet as shown will change the execution policy 
so that you can execute local scripts that you create yourself. Power-
Shell still won’t execute scripts that come from remote sources such as 
email or a website unless they’re signed. Of course, for this check to 
work, the mail tool or the web browser used to do the download must 
set the Zone Identifier Stream to indicate where the file came from. 
Internet  Explorer  and  Microsoft  Outlook  set  this  properly.  At  a
Running elevated
Running elevated is a term used on Windows Vista or later that has to do with the 
User Access Control (UAC) feature added in Vista. It essentially means that you’re 
running with administrative privileges. This can only be done when starting a pro-
cess. Interactively, you can start an elevated PowerShell session by right-clicking the 
PowerShell icon and selecting Run as Administrator. You then get the UAC prompt 
asking if you want to allow this action. 
If you want to run a single command elevated in a script, you can do so with the 
Start-Process cmdlet and the –Verb parameter. For example, you can run Set-
ExecutionPolicy in an elevated PowerShell session as follows:
Start-Process –Verb runas –FilePath powershell.exe 
–ArgumentList 'Set-ExecutionPolicy –ExecutionPolicy RemoteSigned'
When this command is run, you’re prompted to allow the action. If you say yes, a 
new console window appears, the command executes, and the newly created con-
sole window closes after the command is complete.
278
CHAPTER 8
A
DVANCED
FUNCTIONS
AND
SCRIPTS
minimum, I recommend you use the
RemoteSigned
policy. Chapter 17 
covers all these security topics in detail.
Setting the execution policy for a single session
If you can’t run 
Set-ExecutionPolicy
with the necessary administrator privileges 
but you have PowerShell v2 installed, you can use the 
-Scope
parameter on the cmd-
let to just set the execution policy for the current session (the current process). This 
looks like
PS (1) > Set-ExecutionPolicy -Scope process remotesigned
Execution Policy Change 
The execution policy helps protect you from scripts that 
you do not trust. Changing the execution policy might 
expose you to the security risks described in the 
about_Execution_Policies help topic. Do you want to change 
the execution policy?
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"): y 
PS (2) > 
Note the prompt to confirm this operation. You reply 
y
to tell the system to proceed 
to make the change. (You’ll see more on confirmation of actions in section 8.2.2, 
where I show how to implement this feature in scripts.) Now when you try to run 
scripts, they’ll work, but remember, you changed the execution policy only for this 
session. The next time you start PowerShell, you’ll have to rerun the command. 
Okay, now that you’ve got your basic script running, let’s start adding functional-
ity to this script.
8.1.2
Passing arguments to scripts
The first thing we’ll look at is how you can pass arguments to a script. The answer is 
pretty much the same way you did it for basic functions. We’ll start with the 
$args 
variable and look at a modified version of the basic script. Again, you can use redirec-
tion to create the script from the command line. In fact, this version overwrites the 
old version of the script:
PS (8) > '"Hello $args"' > hello.ps1 
and run it with an argument: 
PS (9) > ./hello Bruce 
Hello Bruce
Great—hello PowerShell! But if you don’t supply an argument
PS (10) > ./hello 
Hello
you get a very impersonal greeting. (Notice, by the way, that I didn’t have to specify 
the .ps1 extension when running the script. PowerShell adds this automatically when 
looking for a script file.)
P
OWER
S
HELL
SCRIPTS
279
Let’s see what we can do to make the script a bit chattier. You can take advantage 
of a here-string to generate a slightly longer script:
PS (11) > @' 
>> if ($args) { $name = "$args" } else { $name = "world" } 
>> "Hello $name!" 
>> '@ > hello.ps1 
>>
This script has two lines. The first sets a local variable 
$name
to the value of 
$args
if 
it’s defined. If it’s not defined, it sets 
$name
to 
world
. If you run the script with no 
arguments, you get the generic greeting:
PS (12) > ./hello 
Hello world!
If you run it with an argument, you get a specific greeting:
PS (13) > ./hello Bruce 
Hello Bruce!
PS (14) >
These are the same basic things you did with functions, and, as was the case with 
functions, they have limitations. It would be much more useful to have named, typed 
parameters as  was  the  case  with  functions. But  there’s  a slight  wrinkle: as  you’ll 
remember from chapter 7, the formal arguments to a function are defined outside the 
body of the function, or inside the body with the 
param
statement. Obviously, the 
external definition isn’t going to work with scripts because there’s no “external.” Con-
sequently, there’s only one way to define formal parameters for a script: through the 
param
statement.
Using the param statement in scripts
As mentioned in the previous section, if you want to specify formal parameters for a 
script, you need to use the 
param
statement. The 
param
statement must be the first 
executable line in the script just as it must be the first executable line in a function. 
Only comments and empty lines may precede it. Let’s visit the 
hello
example one 
more time. Again you’ll use a here-string and redirection to create the script. The 
here-string makes it easy to define a multiline script:
PS (14) > @' 
>> param($name="world") 
>> "Hello $name!" 
>> '@ > hello.ps1 
>>
Here you’re adding a second line to the script to declare the script parameter. When 
you run the script, you find the expected results, first with no arguments
PS (15) > ./hello 
Hello world!
Documents you may be interested
Documents you may be interested