Chapter 6 The PowerShell Language

IN THIS CHAPTER

This chapter focuses on performing traditional programming operations with PowerShell. Although many discussions of PowerShell emphasize the new features and capabilities of the language, the designers of the PowerShell language also drew extensively on existing programming and scripting languages to ensure that this new language encompassed the most useful features of these earlier languages. If you are already familiar with the concepts in this chapter, you can use your existing knowledge to become proficient that much more quickly. If you are just getting started with scripting and programming, you can be confident that the effort you put into understanding these language concepts will be applicable to working with PowerShell and can help you create a broader understanding of how programs and scripts accomplish their work. Regardless of your previous level of experience, the examples and discussions covered in this chapter help to illustrate how PowerShell can be used to efficiently accomplish your programming and scripting tasks.

Expressions and Operators

An expression in PowerShell can be understood as a calculation that evaluates an equation and returns a result. An operator in PowerShell is the element of an expression that actually performs the calculation (such as an addition or subtraction operand). Although expressions and operators are closely linked in their usage in PowerShell, this section discusses the implementation details of each of these elements and provides examples to illustrate how operators and expressions work together in PowerShell.

Expressions

The three general categories of expressions that are used in PowerShell are arithmetic expressions (expressions that return a numerical result), assignment expressions (expressions which are used to set, display, and modify variables), and comparison expressions (expressions which use Boolean logic to compare two values and return a True or False result).

Here is a simple example of an arithmetic expression being evaluated by PowerShell:

image

PS C:> 1 + 1
2

image

The following example shows the use of an assignment expression to set the variable $a to the value Example Text String:

image

PS C:> $a = "Example Text String"
PS C:> $a
Example Text String

image

In this next example of a comparison expression, the variable $a is set to the value Example Text String, and the variable $b is set to the value Second Example Text String. The expressions $a -eq $b and $a -ne $b are then evaluated and return a True or False value, as shown in the following:

image

PS C:> $a = "Example Text String"
PS C:> $b = "Second Example Text String"
PS C:> $a -eq $b
False
PS C:> $a -ne $b
True

image

Operators

The PowerShell language contains a substantial number of operators, which are used to construct expressions that return information from PowerShell. Because of the large number of operators that are available in PowerShell, we first review a group of basic operators and then spend time looking at PowerShell’s advanced operators in more detail.

Basic Operators

The six types of basic operators in PowerShell include arithmetic operators, assignment operators, comparison operators, pattern matching operators, regular expression operators, and logical and bitwise operators. One key characteristic of operators in PowerShell is that they can work against many different types of objects (such as numbers, strings, and arrays). For example, the arithmetic operator + can be used to concatenate two text strings into a third string as shown in the following example:

image

PS C:> $a = "This string was concatenated "
PS C:> $b = "using the addition operator."
PS C:> $c = $a + $b
PS C:> $c
This string was concatenated using the addition operator.

image

Table 6.1 describes the arithmetic operators and provides an example of their usage.

Table 6.1. PowerShell Arithmetic Operators

Image

PowerShell’s assignment operators operate similarly to the arithmetic operators described in Table 6.1, but instead of working with two values on the left side of the equation and returning a result on the right side of the equation, assignment operators take a single variable on the left side of the equation, perform an operation against this variable, and then replace the contents of the original variable with the results of the operation (see Table 6.2).

Table 6.2. PowerShell Assignment Operators

Image

Image

The comparison operators in PowerShell are used to compare a pair of variables and return a Boolean True or False result (see Table 6.3). Notice that for each comparison operator (such as -eq), two related operators begin with the letters C and I (such as -ceq and ieq). The prefix C on a comparison operator indicates case sensitivity (for instance, Hello is different from hello), and the I prefix indicates explicit case insensitivity (for instance, Hello and hello are treated the same way). By default, PowerShell’s comparison operators are case insensitive when comparing two strings.

Table 6.3. PowerShell Comparison Operators

Image

Image

The pattern matching operators used in PowerShell also use Boolean logic to compare two strings of text and return a result. To provide more granular comparison abilities, the pattern matching operators work in conjunction with a set of wildcards to create patterns for matching. Four wildcard operators can be used in conjunction with the pattern matching operators -like and -notlike to perform comparisons. The pattern matching operators and their associated wildcards are described in the following two tables.

Table 6.4. PowerShell Pattern Matching Operators

Image

Table 6.5. Characters Used with PowerShell Pattern Matching Operators

Image

The regular expression operators are closely related to the pattern matching operators, and will be a familiar concept to users of VBScript or Perl. However, the regular expression operators (-match, -notmatch, and -replace) are more flexible than pattern matching operators. Regular expression operators support the same type of syntax and wildcards as the pattern matching operators, but the characters used with regular expressions are different from the pattern matching wildcards. These characters are described in the following tables.

Table 6.6. PowerShell Regular Expression Operators

Image

Table 6.7. Characters Used with PowerShell Regular Expressions

Image

Image

The last category of operators that we cover in this section is the PowerShell logical and bitwise operators. These operators support only integer values in the current release of PowerShell, so they are used exclusively for numerical calculations. The logical and bitwise operators available in PowerShell 2.0 CTP are listed in Table 6.8.

Table 6.8. PowerShell Logical and Bitwise Operators

Image

Advanced Operators

In addition to the basic operators described in the previous section, PowerShell also supports several advanced operators. These advanced operators include type operators, unary operators, grouping operators, array operators, property and method operators, format operators, and redirection operators. This section describes each of these operators and provides examples of how they are used in PowerShell operations.

Type operators serve as a method to compare the types of two different objects and return a Boolean True or False value. There is also a type operator to change the type of an object to another type. The following example shows the use of the -is, -isnot, and -as type operators to verify the type of a string, and then it displays a second numerical string as an integer.

image

PS C:> $a = "This is an example string"
PS C:> $a -is [string]
True
PS C:> $a -isnot [string]
False
PS C:> $b = "256"
PS C:> $b -as [int]
256

image

Unary operators are similar to the arithmetic operators discussed in the section on basic operators. The unary operators include +, -, ++, --, |<type>|, and , (the comma). Unary operators can change a number into a positive or negative number, increment or decrement a number, change the type of a variable, or create a single element array. The following example shows each of these unary operators in use:

image

PS C:> $a = 3 * 6
PS C:> -$a
-18
PS C:> $b = "256"
PS C:> +$b
256
PS C:> --$a
PS C:> $a
17
PS C:> ++$a
PS C:> $a
18
PS C:> [int] "0xDEADBEEF"
-559038737
PS C:> ,(3*7)
21
PS C:>

image

Grouping operators are used in PowerShell to bring together a set of terms and perform an operation against these terms. The three types of grouping operators include parentheses (used to group expression operators), the subexpressions grouping operator (the $ symbol, used to group together collections of statements), and the array subexpressions operator (the @ symbol, used to group together collections of statements and insert the results into an array). Each of these grouping operators is demonstrated in the following example:

image

Image

image

The array operators are a set of PowerShell operators that enable arrays to be created, modified, and reordered. Array operators can work with arrays that are one-dimensional or multidimensional. Because the use of array operators is so closely linked to working with arrays, we discuss the use of array operators in the section on arrays later in this chapter.

Property and method operators are some of the most commonly used operators in PowerShell. The basic property operator is the period (.), which is used to access properties of a variable. In the following example, the string $a is set to 12345, and then the period operator is used to access the Length property of the variable $a using the syntax $a.Length.

image

PS C:> $a = "12345"
PS C:> $a.Length
5

image

NOTE

When you are working with properties of a variable, one of the helpful features of PowerShell is the tab completion feature. Tab completion enables you to enter the variable name, the period property operator, and then press the Tab key to cycle through the available properties for that variable. Try setting a variable in a PowerShell session, enter just the variable name and a period (as in $a.), and then press the Tab key repeatedly to view the property options for the variable.

The PowerShell method operators are frequently used in conjunction with the property operators to provide finer control over the data that is returned from a property query. The basic syntax for using method operators with the period property operator is as follows:

image

image

The next example shows the string variable $b being set to 1234567890, and then shows the SubString method being called with an argument of (0,4) to return the first four characters of the $b string.

image

PS C:> $b = "1234567890"
PS C:> $b.substring(0,4)
1234

image

A second frequently used PowerShell method operator is the double colon (::). The double colon is used to access members of a specific class of objects, and it is technically referred to as a static member accessor. Because the double colon method operator is working with members of a class, the argument on the left side of the double colon must be a valid type name, and the argument on the right side must be a valid member name for the class on the left, as in the example [string]::Equals. To obtain a list of valid methods for a given type, you can use the command [<type-name>] | Get-Member -static, replacing <type-name> with the specific type in which you are interested. The following example shows the available static methods for the char type using the command [char] | Get-Member -static.

image

Image

Image

image

After you have identified the available methods for a particular type, you can select the particular method that you want for that type, and then access it using the double colon operator. The next example shows the variable $c being set to A (capital letter A), and then the char type method ToLower being called against the $c variable to convert the variable from upper case to lower case. The second example shows the variable $d being set to a (lower case A), and then the char method ToUpper being called to convert this variable from lower to upper case.

image

PS C:> $c = "A"
PS C:> [char]::ToLower($c)
a
PS C:> $d = "a"
PS C:> [char]::ToUpper($d)
A

image

The format operator in PowerShell is used to provide more granular control over PowerShell’s output. The basic structure of a format operator statement includes a format string on the left and an array of values on the right, as shown in the following example:

image

image

NOTE

Note that the values on the right side of a format operator statement are not limited to being numeric (character and string values are also supported). However, most of the advanced format string arguments operate primarily against numerical values.

The following formatting examples show a list of three fruit names being output in reverse order (apples bananas cherries becomes cherries bananas apples) and three sets of numbers being displayed in a different order than they were originally entered (123 456 789 becomes 123 789 456). The last example shows a list of three items (Item1, Item2, and Item3) and the resulting output when the format string includes only the second element of the array.

image

PS C:> '{2} {1} {0}' -f "apples","bananas","cherries"
cherries bananas apples
PS C:> '{0} {2} {1}' -f "123","456","789"
123 789 456
PS C:> '{1}' -f "Item1","Item2","Item3"
Item2
PS C:>

image

So, at a basic level, the format operator can be used to control the order in which elements of an array are displayed, or which elements are displayed at all. However, the format operator can also provide a tremendous number of other output options, simply by applying different arguments to the format strings. The general syntax for format string arguments is {0:<argument>}, where 0 is replaced by the array element that you are interested in, and <argument> is replaced by the format string argument that you want to apply to the data in the array element. Table 6.9 illustrates some of the possible arguments for format strings, describes what each of them does, and provides a usage example for each argument.

Table 6.9. Selected Format String Arguments

Image

Although the number of possible format operators is too great to be covered in the scope of this chapter, MSDN provides a comprehensive reference on the syntax for the use of arguments in .NET Framework formatting strings in the .NET Framework Developer’s Guide in the section on Formatting Types. This guide can be found by visiting msdn.microsoft.com and searching on the keywords “Formatting Types.” Through the use of these and other format operators, a wide variety of format string elements can be created to meet your formatting requirements.

The final advanced operators that we cover in this section are the redirection operators. The redirection operators are used to direct command output to another location, such as a file. The next example shows the output of the PowerShell command Get-Process s* being redirected to the text file C:S-Processes.txt. The file C:S-Processes.txt is then displayed at the command line using the Get-Content command.

image

Image

image

NOTE

One major difference in PowerShell’s redirection operators versus other shells is that the < (input redirection) operator is currently not implemented as of PowerShell 2.0 CTP2. A syntax error is returned if you attempt to use an input redirection operator in a PowerShell command.

Table 6.10 describes each of the redirection operators available in PowerShell 2.0 CTP2 and provides examples of their usage.

Table 6.10. PowerShell Redirection Operators

Image

Escape Sequences

The grave-accent or backtick (`) acts as the PowerShell escape character. Depending on when this character is used, PowerShell interprets characters immediately following it in a certain way.

If the backtick character is used at the end of a line in a script, it acts as a continuation character. In other words, ` acts the same way & does in VBScript, enabling you to break long lines of code into smaller chunks, as shown here:

image

image

If the backtick character precedes a PowerShell variable, the characters immediately following it should be passed on without substitution or processing:

image

PS C:> $String = "Does this work?"
PS C:> write-host "The question is: $String"
The question is: Does this work?
PS C:> write-host "The question is: `$String"
The question is: $String
PS C:>

image

If the backtick character is used in a string or interpreted as part of a string, that means the next character should be interpreted as a special character. For example, if you want to place a TAB in your string, you use the `t escape character sequence, as shown here:

image

PS C:> $String = "Look at the tab:`t [TAB]"
PS C:> write-host $string
Look at the tab:         [TAB]
PS C:>

image

Table 6.11 lists the escape character sequences supported by PowerShell.

Table 6.11. PowerShell Escape Sequences

Image

Error Handling

PowerShell errors are divided into two types: terminating and nonterminating. Terminating errors, as the name implies, stop a command. Nonterminating errors are generally just reported without stopping a command. Both types of errors are reported in the $Error variable, which is a collection of errors that have occurred during the current PowerShell session. This collection contains the most recent error, as indicated by $Error[0] up to $MaximumErrorCount, which defaults to 256.

Errors in the $Error variable can be represented by the ErrorRecord object. It contains error exception information and several other properties that are useful for understanding why an error occurred

The next example shows the information that is contained in InvocationInfo property of an ErrorRecord object:

image

PS C:> $Error[0].InvocationInfo


MyCommand        : Get-ChildItem
ScriptLineNumber : 1
OffsetInLine     : -2147483648
ScriptName       :
Line             : dir z:
PositionMessage  :
                   At line:1 char:4
                   + dir  <<<< z:
InvocationName   : dir
PipelineLength   : 1
PipelinePosition : 1



PS C:>

image

Based on this information, you can determine a number of details about $Error[0], including the command that caused the error to be thrown. This information is crucial to understanding errors and handling them effectively.

Use the following command to see a full list of ErrorRecord properties:

image

Image

image

Table 6.12 shows the definitions for each of the ErrorRecord properties that are listed in the preceding example:

Table 6.12. ErrorRecord Property Definitions

Image

Methods for Handling Errors in PowerShell

Methods for handling errors in PowerShell can range from simple to complex. The simple method is to allow PowerShell to handle the error. Depending on the type of error, the command or script might terminate or continue. However, if the default error handler doesn’t fit your needs, you can devise a more complex error-handling scheme by using the methods discussed in the following sections.

Method One: cmdlet Preferences

In PowerShell, ubiquitous parameters are available to all cmdlets. Among them are the ErrorAction and ErrorVariable parameters, used to determine how cmdlets handle nonterminating errors, as shown in this example:

image

PS C:> get-childitem z: -ErrorVariable Err -ErrorAction
SilentlyContinue
PS C:> if ($Err){write-host $Err -Foregroundcolor Red}
Cannot find drive. A drive with name 'z' does not exist.
PS C:>

image

The ErrorAction parameter defines how a cmdlet behaves when it encounters a nonterminating error. In the preceding example, ErrorAction is defined as SilentlyContinue, meaning the cmdlet continues running with no output if it encounters a nonterminating error. Other options for ErrorAction are as follows:

Continue—Print error and continue (default action)

Inquire—Ask users whether they want to continue, halt, or suspend

Stop—Halt execution of the command or script

NOTE

The term nonterminating has been emphasized in this section because a terminating error bypasses the defined ErrorAction and is delivered to the default or custom error handler.

The ErrorVariable parameter defines the variable name for the error object generated by a nonterminating error. As shown in the previous example, ErrorVariable is defined as Err. Notice the variable name doesn’t have the $ prefix. However, to access ErrorVariable outside a cmdlet, you use the variable’s name with the $ prefix ($Err). Furthermore, after defining ErrorVariable, the resulting variable is valid for the current PowerShell session or associated script block. This means other cmdlets can append error objects to an existing ErrorVariable by using a + prefix, as shown in this example:

image

PS C:> get-childitem z: -ErrorVariable Err -ErrorAction
SilentlyContinue
PS C:> get-childitem y: -ErrorVariable +Err -ErrorAction
SilentlyContinue
PS C:> write-host $Err[0] -Foregroundcolor Red
Cannot find drive. A drive with name 'z' does not exist.
PS C:> write-host $Err[1] -Foregroundcolor Red
Cannot find drive. A drive with name 'y' does not exist.
PS C:>

image

Method Two: Trapping Errors

When encountering a terminating error, PowerShell’s default behavior is to display the error and halt the command or script execution. If you want to use custom error handling for a terminating error, you must define an exception trap handler to prevent the terminating error (ErrorRecord) from being sent to the default error-handling mechanism. The same holds true for nonterminating errors as PowerShell’s default behavior is to just display the error and continue the command or script execution.

To define a trap, you use the following syntax:

image

image

The first part is ExceptionType, which specifies the type of error a trap accepts. If no ExceptionType is defined, a trap accepts all errors. The code part can consist of a command or set of commands that run after an error is delivered to the trap. Defining commands to run by a trap is optional. The last part, keyword, is what determines whether the trap allows the statement block where the error occurred to execute or terminate.

Supported keywords are as follows:

Break—Causes the exception to be rethrown and stops the current scope from executing

Continue—Enables the current scope execution to continue at the next line where the exception occurred

Return [argument]—Stops the current scope from executing and returns the argument, if specified

If a keyword isn’t specified, the trap uses the keyword Return [argument]; argument is the ErrorRecord that was originally delivered to the trap.

Trap Examples

The following two examples show how traps can be defined to handle errors. The first trap example shows a trap being used in conjunction with a nonterminating error that is produced from an invalid DNS name being given to the System.Net.Dns class. The second example shows a trap being again used in conjunction with a nonterminating error that produced from the Get-Item cmdlet. However, in this case, because the ErrorAction parameter has been defined as Stop, the error is a terminating error that is then handled by the trap.

Example one: errortraps1.ps1

image

image

The $_ parameter in this example represents the ErrorRecord that was delivered to the trap.

Output:

image

PS C:> .errortraps1.ps1
Getting IP address for www.-baddnsname-.com
ERROR: Exception calling "GetHostAddresses" with "1" argument(s): "No
such host
is known"
Done Getting IP Address
PS C:>

image

Example two: errortraps2.ps1

image

image

NOTE

A cmdlet doesn’t generate a terminating error unless there’s a syntax error. This means a trap doesn’t catch nonterminating errors from a cmdlet unless the error is transformed into a terminating error by setting the cmdlet’s ErrorAction to Stop.

Output:

image

:> .errortraps2.ps1
Changing drive to z:
[ERROR] Command execution stopped because the shell variable
"ErrorActionPrefere
nce" is set to Stop: Cannot find drive. A drive with name 'z' does not
exist.
Done getting items
PS C:>

image

Trap Scopes

A PowerShell scope determines how traps are executed. Generally, a trap is defined and executed within the same scope. For example, you define a trap in a certain scope; when a terminating error is encountered in that scope, the trap is executed. If the current scope doesn’t contain a trap and an outer scope does, any terminating errors encountered break out of the current scope and are delivered to the trap in the outer scope.

Method Three: The Throw Keyword

In PowerShell, you can generate your own terminating errors. This doesn’t mean causing errors by using incorrect syntax. Instead, you can generate a terminating error on purpose by using the throw keyword, as shown in the next example if a user doesn’t define the argument for the MyParam parameter when trying to run the MyParam.ps1 script. This type of behavior is useful when data from functions, cmdlets, data sources, applications, and so on is not what is expected and can prevent the script or set of commands from executing correctly further into the execution process.

Script:

image

image

Output:

image

PS C: .MyParam.ps1
You did not define MyParam
ScriptHalted
At C:MyParam.ps1:1 char:33
+ param([string]$MyParam = $(throw  <<<< write-host "You did not
define MyParam
" -Foregroundcolor Red))
PS C:>

image

Managing Elements with Arrays

Arrays are a frequently used construct in all programming languages and PowerShell. At its simplest level, an array is just a collection of objects. In programming terms, an array can be seen as a data structure that consists of a group of elements. These elements are indexed and accessed by referencing the index number of the element. Arrays can be one-dimensional (a collection of elements that can be accessed individually by specifying a single index value, such as a single column of numbers) or multidimensional (a collection of elements that require multiple index values to specify a particular element, such as a table of numbers).

PowerShell has a set of operators specifically for working with arrays and collections. The most frequently used array operator is the comma (,). The comma operator is used to separate elements in a one-dimensional array. The next example shows the integers 0,1,2,3,4,5 being stored as a one-dimensional array in the variable $a.

image

PS C:> $a = 0,1,2,3,4,5
PS C:> $a
0
1
2
3
4
5

image

NOTE

Unlike many other programming languages, PowerShell does not contain a specific syntax for defining an array (also known as an array literal). Instead, the PowerShell array operators are used to create collections of elements, which can then be accessed by assigning the collection to a variable. In the previous example, the PowerShell command $a=0,1,2,3,4,5 creates a one-dimensional array assigned to the variable $a. A slightly different syntax is used to create multidimensional arrays, which are discussed later in this section.

Another way to create a one-dimensional array in PowerShell is through the use of the array subexpression operator, discussed earlier in this chapter in the “Advanced Operators” section. The array subexpression operator follows the syntax @(element1,element2) and inserts the elements in parentheses into an array. The following example shows the variable $a being populated with the contents of the subexpression operator @(1,2,3,4,5).

image

PS C:> $a=@(1,2,3,4,5)
PS C:> $a
1
2
3
4
5

image

As with all the PowerShell array operators, the array subexpression operator is not restricted to working with integers. For example, it is possible to store the output of PowerShell cmdlets as elements in an array using the array subexpression operator. However, it might be necessary to use the semicolon as a separator in this instance (which PowerShell recognizes as a command terminator), which is not the case when using the comma. The following example shows the results of running the command $a=@(get-process s*,get-date,get-service D*), which attempts to run three separate PowerShell commands that list all processes beginning with the letter S, list the current date, and list all services beginning with the letter D. When this command is run, the parser interprets the second two commands as parameters of the Get-Process command and returns an error, as shown here:

image

PS C:> $a=@(get-process s*,get-date,get-service D*)
Get-Process : A parameter cannot be found that matches parameter name
'D*'.
At line:1 char:17
+ $a=@(get-process <<<<  s*,get-date,get-service D*)

image

To ensure that PowerShell correctly interprets the sequence of commands and is able to store each of the commands as elements in the array, the command can be reentered as $a=@(get-process s*;get-date;get-service D*). This is the same command as in the previous example, but the semicolon is used as a separator here instead of the comma. As you can see from the next example, the command is successful and the output of the three PowerShell commands is stored in the variable $a using the array subexpression operator.

image

Image

Image

image

NOTE

An interesting point about the previous array subexpression operator command that captures the output of three PowerShell cmdlets is that each returned value is stored as its own element in the array. For example, the services process is the first element of the array, and the DNS Client service is the last element of the array. The number of elements in the array is determined dynamically by PowerShell based on the number of values returned from the three cmdlets.

After an array has been created, you can access elements of the array by referring to the variable containing the array and specifying the element you want using the element’s index value enclosed in square brackets ([ ]). The next example shows an one-dimensional array $b being created with the elements "apricots","blueberries","cranberries","dates","elderberries","figs", and then a second command $b[2] is used to retrieve the third element of the array. (Array element numbering begins at 0, so the six elements of our example array $b have an index value ranging from 0 to 5.)

image

PS C:> $b =
"apricots","blueberries","cranberries","dates","elderberries","figs"
PS C:> $b[2]
Cranberries

image

PowerShell also includes operators to work with a range of array elements and to work with array elements in reverse order. The PowerShell range operator (..) is used to specify a range of elements, and the hyphen (-) is used to indicate a negative index, which accesses array elements starting from the end of the array instead of the beginning. We go through a couple of examples to show how these operators work.

The first example defines an array of fruit names in the variable $b, then issues the command $b[3..5], which displays the elements of array $b with an index value between 3 and 5 (dates, elderberries, and figs). Next, the command $b[-1] is issued to display the last element of the array (figs), and you can also see that the command $b[-2] displays the second-to-last element of the array (elderberries). The final example shown combines the range operator and the hyphen in the command $b[-1..-3], which displays the last three elements of the array in reverse order (figs, elderberries, and dates).

image

PS C:> $b =
"apricots","blueberries","cranberries","dates","elderberries","figs"
PS C:> $b[3..5]
dates
elderberries
figs

PS C:> $b[-1]
figs

PS C:> $b[-2]
elderberries

PS C:> $b[-1..-3]
figs
elderberries
dates

image

Another technique for working with array elements in PowerShell is known as array slicing. Array slicing is similar to using the range operator, but it is possible to select array elements that are not sequential. Assuming that the array $b exists and has at least five elements, the syntax for using the array slicing technique is as follows:

image

image

Executing this command will return the array elements in array $b with the index values of 0,2,4. The next example defines an array of six fruit names in the variable $b, uses array slicing to return the array elements in array $b with the index values of 0, 2, and 4, and then uses the elements with the index values of 1, 3, and 5.

image

PS C:> $b =
"apricots","blueberries","cranberries","dates","elderberries","figs"
PS C:> $b[0,2,4]
apricots
cranberries
elderberries

PS C:> $b[1,3,5]
blueberries
dates
figs

image

As you can see from the previous examples, it is possible to use the comma operator to create arrays containing numeric or text elements without needing to specify the type of data that the array will contain. Although it is possible to restrict the type of data that can be stored in an array, by default, PowerShell allows any type of data to be contained in an array element, and it can even store different data types in elements contained within the same array. The following example shows the array $c being created with the elements 1,3.1416,0xF,"text","","string". Note that when the array elements are listed, PowerShell converts the hexadecimal value for 0xF to the decimal value 15. You can also use any of the PowerShell operators described earlier in this chapter to work with array elements, such as addition or division, as shown in the following examples. However, your array elements must be type-compatible for the operation to be successful. The final example shows the results of attempting to add a numeric value to a string.

image

PS C:> $c = 1,3.1416,0xF,"text"," ","string"
PS C:> $c
1
3.1416
15

text

string
PS C:> $d = $c[3] + $c[4] + $c[5]
PS C:> $d
text string
PS C:> $e = $c[2] / $c[1]
PS C:> $e
4.7746371275783
PS C:> $f = $c[0] + $c[5]
Cannot convert value "string" to type "System.Int32". Error: "Input
string was not in a correct format."
At line:1 char:13
+ $f = $c[0] + <<<<  $c[5]

image

A multidimensional array is a collection of elements that require multiple index values to identify a particular element. The following grid represents a simple multidimensional array that contains nine elements. When referring to a particular array element in a multidimensional array, the convention is to specify the row index value first, and then the column index value. For example, the index value pair 1,0 returns a value of 4 when applied to the example array shown here. The index value pair 2,1 returns a value of 8.

Image

PowerShell handles multidimensional arrays somewhat differently from one-dimensional arrays. Whereas a one-dimensional array can be constructed simply by the use of the comma operator, a multidimensional array must be created using the New-Object cmdlet using the following syntax:

image

image

The next example creates a two-dimensional array identical to the one shown in the previous grid. You can see that as in the previous grid, the element stored at row 1, column 0 has a value of 4, whereas the element stored at row 2, column 1 has a value of 8.

image

PS C:> $2_dimensional_array = new-object 'object[,]' 3,3
PS C:> $2_dimensional_array[0,0] = 1
PS C:> $2_dimensional_array[0,1] = 2

PS C:> $2_dimensional_array[0,2] = 3
PS C:> $2_dimensional_array[1,0] = 4
PS C:> $2_dimensional_array[1,1] = 5
PS C:> $2_dimensional_array[1,2] = 6
PS C:> $2_dimensional_array[2,0] = 7
PS C:> $2_dimensional_array[2,1] = 8
PS C:> $2_dimensional_array[2,2] = 9
PS C:> $2_dimensional_array[1,0]
4
PS C:> $2_dimensional_array[2,1]
8
PS C:>

image

Other than the need to refer to elements of a multidimensional array using multiple index values, multidimensional arrays function the same way as one-dimensional arrays and can be managed using the same operators and techniques that apply to one-dimensional arrays.

Creating Functions

In PowesrShell, functions are one of four categories of commands. (The other three categories are cmdlets, scripts, and native commands.) A function is a uniquely named piece of script code that resides in memory while PowerShell is running, but is deleted from memory when PowerShell closes.

At its most basic level, a PowerShell function can be defined at the command line using the following syntax:

image

image

When defining a function, all the elements shown in the previous example are required except for the function-parameters argument, which is optional.

The next example shows a simple function called my-function being created at the PowerShell command line to illustrate the basic syntax of creating a function.

image

PS C:> function my-function { "This is a function."}
PS C:> my-function
This is a function.
PS C:>

image

PowerShell also enables more complex functions to be created at the command line. You can demonstrate this functionality by entering the following syntax at the PowerShell command prompt:

image

image

After you press Enter after the curly brace, the prompt changes to >> and you can enter your function statements on multiple lines. After you have completed entering your function, you can enter a closing curly brace (}) on a line by itself, and then press Enter to return to the standard PowerShell command prompt. The following example shows this technique being used to create a function named multiline-function.

image

PS C:> function multiline-function {
>> $a = 1
>> $b = 2
>> $c = 3
>> $a
>> $b
>> $c
>> $a + $b + $c
>> }
>>
PS C:> multiline-function
1
2
3
6
PS C:>

image

Although creating these simple functions can be instructive, they aren’t particularly useful for day-to-day work in PowerShell. One of the most powerful capabilities that PowerShell provides is the use of the pipeline to direct the output of one command into another. Let’s take a look at a slightly more complex function that is designed to accept input from the PowerShell pipeline.

image

PS C:> function add-numbers {
>> $sum_of_numbers = 0;
>> foreach ($number in $input) {$sum_of_numbers += $number}
>> $sum_of_numbers
>> }
>>
PS C:> 1,2,3 | add-numbers

6
PS C:> 1,3,5,7,9 | add-numbers
25
PS C:> 1..10 | add-numbers
55
PS C:> 1..100 | add-numbers
5050
PS C:>

image

The previous function is named Add-Numbers. The first thing this function does is to initialize the variable $sum_of_numbers to 0. It stores any data that it receives in a variable called $input, and then takes each data element in $input and adds it to $sum_of_numbers. After it has completed parsing the data in $input, it returns the current value of $sum_of_numbers to the command line.

In the examples of using the Add-Numbers function, you can see that the function can accept numerical input separated by commas or operate against a range of numbers using the range operator, as we discussed in the section on arrays. In all cases, the Add-Numbers function takes the data provided, adds each element to the $sum_of_numbers variable, and displays the total.

The next type of function that we focus on in this section is a function that can perform the same type of actions that a cmdlet does. Because compiled cmdlets follow a specific order of operations, functions that act like cmdlets must operate in the same way. The following diagram shows the general structure that this type of function must follow:

image

image

This function template defines three categories of actions that are performed by the function: begin actions, process actions, and end actions. Statements specified in the begin section are executed before any objects in the pipeline are evaluated by the function. The statements in the process section are executed once for each object in the pipeline. Finally, the statements in the end section are executed after the process section has completed and no more objects are present in the pipeline. The next example function follows this template and helps to illustrate how this sequence of events unfolds:

image

PS C:> function example-cmdlet ($a) {
>> begin {$p=0; "Begin Phase: Process iteration count is $p, cmdlet
argument is $a"}
>> process {$p++; "Process Phase: Process iteration count is $p,
pipeline element is $_, cmdlet argument is $a"}
>> end {"End Phase: Process iteration count is $p, cmdlet argument is
$a"}
>> }

image

This function is configured to accept a single command-line parameter, which is the variable $a. It can also receive and process input from the pipeline. In the following example, a list of five numbers is entered and piped to the example-cmdlet function that has a parameter of 7, as shown here:

image

image

When this command is executed, the example-cmdlet function goes through its three phases and reports on the values for process iteration count, the cmdlet argument, and the element in the pipeline during the process phase.

image

PS C:> 2,4,6,8,10 | example-cmdlet 7
Begin Phase: Process iteration count is 0, cmdlet argument is 7
Process Phase: Process iteration count is 1, pipeline element is 2,
cmdlet argument is 7
Process Phase: Process iteration count is 2, pipeline element is 4,
cmdlet argument is 7
Process Phase: Process iteration count is 3, pipeline element is 6,
cmdlet argument is 7
Process Phase: Process iteration count is 4, pipeline element is 8,
cmdlet argument is 7
Process Phase: Process iteration count is 5, pipeline element is 10,
cmdlet argument is 7
End Phase: Process iteration count is 5, cmdlet argument is 7
PS C:>

image

This next example does a better job of demonstrating the capabilities of this type of function. In the following command, the output of the command get-process s* is piped to the example-cmdlet function, which has a parameter of (get-process explorer).CPU, as shown here:

image

image

When this command executes, the example-cmdlet function again goes through its three phases, but both the pipeline ($p) and the function argument ($a) are dynamically obtained from the PowerShell environment instead of being statically assigned, as in the first example.

image

PS C:> (get-process s*) | example-cmdlet (get-process explorer).CPU
Begin Phase: Process iteration count is 0, cmdlet argument is
389.409944
Process Phase: Process iteration count is 1, pipeline element is
System.Diagnostics.Process (services), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 2, pipeline element is
System.Diagnostics.Process (smss), cmdlet argument is
389.409944
Process Phase: Process iteration count is 3, pipeline element is
System.Diagnostics.Process (spoolsv), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 4, pipeline element is
System.Diagnostics.Process (svchost), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 5, pipeline element is
System.Diagnostics.Process (svchost), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 6, pipeline element is
System.Diagnostics.Process (svchost), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 7, pipeline element is
System.Diagnostics.Process (svchost), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 8, pipeline element is
System.Diagnostics.Process (svchost), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 9, pipeline element is
System.Diagnostics.Process (svchost), cmdlet argument
is 389.409944

Process Phase: Process iteration count is 10, pipeline element is
System.Diagnostics.Process (svchost), cmdlet argument
is 389.409944
Process Phase: Process iteration count is 11, pipeline element is
System.Diagnostics.Process (System), cmdlet argument
is 389.409944
End Phase: Process iteration count is 11, cmdlet argument is
389.409944
PS C:>

image

After you work with functions and create some functions that are useful for you, you will most likely want to use them again in future. PowerShell provides a simple way to accomplish this by saving functions as text files. The following example shows the example-cmdlet function that we previously worked with saved as a PowerShell script file named MyFunctions.ps1:

image

image

There are a couple of items to note about the previous script. The first three lines of the script are preceded by the # symbol, which is used to add comments to a script. Also, on line 4 of the script, you will notice that a global: keyword has been prefixed to the function name. This is done to ensure that the example-cmdlet function is added to PowerShell’s global scope. Additional functions can be added to the MyFunctions.ps1 script following the standard function syntax and adding the global: prefix to the function name. Finally, it is important to note that the only thing that MyFunctions.ps1 does is to define a global function named example-cmdlet—it does not generate any output of its own or perform any other tasks. Let’s take a look at how to use MyFunctions.ps1 to load a function into memory.

In the next example, the script file MyFunctions.ps1 has been saved to the folder C:scripts. The script is executed by entering the full path to the script file at the PowerShell command prompt. After the MyFunctions.ps1 script is run, you can confirm that the example-cmdlet function was loaded into memory by using PowerShell’s function provider. (More details on PowerShell’s providers are available in the section on providers in Chapter 4, “Other Key PowerShell Concepts.”)

image

PS C:> C:scriptsMyFunction.ps1
PS C:> dir function:/example-cmdlet

CommandType     Name                              Definition
-----------     ----                              ----------
Function        example-cmdlet                    param($a) begin {...

image

After the example-cmdlet function has been loaded into memory, it can be used just as if it had been entered from the command line. If you find that you need to have a certain function available every time PowerShell starts, the function can be added to a PowerShell profile script such as %UserProfile%\My DocumentsWindowsPowerShellMicrosoft.PowerShell_profile.ps1 so that it will be loaded automatically at startup. (For more information on PowerShell profiles, please consult the section on profiles in Chapter 4.)

Understanding Filters

Filters in PowerShell are similar to functions. The primary difference between filters and functions is that although a function in a pipeline runs once after all the input has been gathered, a filter runs once for each element in the pipeline. As with a function, a PowerShell filter is defined using the following syntax:

image

image

The key functional difference between functions and filters is the way that data from the pipeline is handled. Because filters are designed to process a single pipeline element at a time, they are ideal for evaluating a large amount of data from the pipeline and returning a subset of the pipeline data. Filters use a special variable $_ to operate on the current element in the pipeline. The following example shows the basic layout of a PowerShell filter:

image

image

Looking at the previous example, one way to understand a PowerShell filter is to think of it as a function with only a process clause (that is, no begin or end clauses). The next example shows the creation of a simple filter named add-one that increments the current value of each number in the pipeline by one:

image

PS C:> filter add-one {$_+1}
PS C:> 1,2,3,4,5 | add-one
2
3
4
5
6
PS C:>

image

Controlling Script Flow with Loops

A loop in PowerShell is a method for controlling the logical flow and code execution within a script. Several different types of loops are available in PowerShell. These loops include the while loop, the do/while loop, the for loop, and the foreach loop. This section covers each of these methods and provides usage examples for each method.

A while loop in PowerShell executes a set of statements as long as a given condition is true. The syntax of the while loop is shown here:

image

image

An example of a while loop in operation is shown in the following example. The variable $myvar is initially set to 2 and is multiplied by 2 with each iteration of the while loop. The loop continues as long as the value of the variable $myvar is not equal to 32.

image

PS C:> $myvar=2; while ($myvar -ne 32){$myvar*=2; "The value of
`$myvar is $myvar"}
The value of $myvar is 4
The value of $myvar is 8
The value of $myvar is 16
The value of $myvar is 32
PS C:>

image

A do/while loop in PowerShell is similar to a while loop. The main difference between these two types of loop is that in a do/while loop, an action is performed before a condition is checked. The syntax of the do/while loop is shown here:

image

image

The following example shows a do/while version of the previous while loop example:

image

PS C:> $myvar=2; do {$myvar*=2; "The value of `$myvar is $myvar"}
while ($myvar -ne 32)
The value of $myvar is 4
The value of $myvar is 8
The value of $myvar is 16
The value of $myvar is 32
PS C:>

image

for loops in PowerShell are generally used for counting or iterating through a collection of objects one by one. This can be useful when it is necessary to know the exact element you are working with in a list or collection. The general syntax for a for loop is shown here:

image

image

The three values between the parentheses (loop_counter_initial_value, loop_counter_evaluation, and increment_loop_counter) define the behavior of the for loop. The next example shows the initial value of the loop counter ($z) being set to 0, the loop counter evaluation being set to continue if $z does not equal 10, and the loop counter incrementing $z by 1 during each iteration. As long as $z does not equal 10, a message is printed out indicating the current value of $z.

image

PS C:> for ($z=0; $z -ne 10; $z++) {"The value of `$z is $z."}
The value of $z is 0.
The value of $z is 1.
The value of $z is 2.
The value of $z is 3.
The value of $z is 4.
The value of $z is 5.
The value of $z is 6.
The value of $z is 7.
The value of $z is 8.
The value of $z is 9.
PS C:>

image

Another type of loop provided by PowerShell is the foreach loop. As with a for loop, a foreach loop is most frequently used to operate on collections of objects. However, a foreach loop has the advantage of not requiring a separate counter to manage the iteration of the loop. The general syntax for a foreach loop is shown here:

image

image

This syntax provides more flexibility than a standard for loop for several reasons. First, the element and the pipeline do not have to be numerical values—they can be any valid PowerShell data type. Second, the foreach loop does not require a specific range of data to be defined before the foreach loop executes. In this sense, the foreach loop can operate more like a query than a loop. The next example uses a foreach loop to count the number of *.ps1 files in a directory named c:scripts and uses the number of files returned to govern how many times the foreach loop iterates. While it runs, the loop accumulates the number of files returned in the variable $number and accumulates the total size of the returned files in the variable $size.

image

PS C:> $number=0;$size=0;foreach ($file in dir c:scripts*.ps1)
{$number += 1; $size += $file.length}
PS C:> $number
4
PS C:> $size
1372
PS C:> dir c:scripts*.ps1


    Directory: Microsoft.PowerShell.CoreFileSystem::C:scripts


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         6/18/2008   3:16 PM        211 ListNouns.ps1
-a---         6/18/2008   2:08 PM        211 ListVerbs.ps1
-a---         7/14/2008  11:32 AM        310 myfunction.ps1
-a---         7/14/2008  11:23 AM        640 testps1.ps1


PS C:>

image

Using Logic and Making Decisions

PowerShell provides several different options for using logic to make decisions within functions and scripts. In addition to the basic if/else loops that are familiar to users of other programming languages, PowerShell enables scripters to implement decision making in their scripts using labels and keywords, such as break and continue, to control script behavior. PowerShell also implements a construct known as the switch statement for use in decision making, which is similar to the VBScript SELECT CASE statement but provides more functionality because of its highly flexible matching options. This section describes each of these methods and gives usage examples.

The if/else loop is the most basic conditional statement in PowerShell and is most likely familiar to most users of other programming languages. The general syntax for if/else loops in PowerShell is as follows:

image

image

The following example uses the if, elseif, and else keywords in conjunction with the -gt comparison operator to evaluate a variable named $mynum:

image

image

Scripts in PowerShell can also use labels to make decisions about script flow. Labels can be used in any of the loop types that have been discussed thus far, but are most commonly used in while loops to enable script execution to halt when a certain condition is met. Labels are frequently used in conjunction with the break and continue keywords to direct script execution to a particular point in the script. The following example shows the break keyword being used to direct the script execution from within a nested loop to the :exit label:

image

image

Another construct used for decision making in PowerShell is the switch statement. The switch statement is used to perform a selection based on a value, and then it executes an action based on the value returned. The following is the syntax for the switch statement:

image

image

The -options argument for the switch statement can support wildcards and regular expressions, in addition to enabling the user to specify case sensitivity in matches, exact matches only, or to scan a file instead of a set of objects. The following example shows a basic usage of the switch statement to identify a specific type of fruit based on an input value:

image

image

In the previous example, the variable $fruit is set to 7, so the switch statement matches on the statement "The fruit is a grape". If the value of $fruit were set to a number that was not between 1 and 7, the default choice would be returned because the value passed to the switch statement did not match any of the available options.

A more complex usage of the switch statement is shown next. In this case, the variable $fruit is set to date, and the –wildcard option is added to the switch statement to enable string matching against the $fruit argument. To allow the wildcard match to succeed, the patterns preceding the seven fruit names have been modified to follow the syntax a*, which allows matching on the first letter of the $fruit variable.

image

image

The other commonly used option for the switch statement is the -regex option, which matches based on regular expressions. In this example, the variable $fruit is set to G32790 to represent a product code for a particular type of fruit. The patterns for the seven fruit names have been changed to include a range of letters that allow matching on the first letter of the product code stored in the $fruit variable.

image

image

Building Scripts with Scriptblocks

In PowerShell, a scriptblock is a construct that groups statements and commands into a single block of code. Scriptblocks are always contained between a set of braces ({ }). In this respect, scriptblocks are nearly identical to functions, with the notable exception that scriptblocks do not have names. Instead of having names, scriptblocks are assigned to variables to work with them. Many of the programming examples in this chapter have made use of scriptblocks, but this section covers their usage in detail.

In PowerShell, scriptblocks are functions without names, and functions are scriptblocks with names. You can demonstrate this at any PowerShell command prompt by obtaining the full name of any of the built-in PowerShell functions. In the following example, the command $function:help.gettype().Fullname is used to return the full type name of the built-in PowerShell help function, which is reported as System.Management.Automation.ScriptBlock.

image

PS C:> $function:help.gettype().Fullname
System.Management.Automation.ScriptBlock
PS C:>

image

At its most basic level, a PowerShell scriptblock can be defined using the following syntax:

image

image

The only required information to create a scriptblock is the statement-list parameter—all other information is optional. Referring back to our earlier discussion on functions, it is possible to create a scriptblock that acts like a cmdlet by including the begin, process, and end clauses, as shown in the following example:

image

image

Because scriptblocks in PowerShell do not have names, they must be assigned to variables in order to work with them. To execute a scriptblock that is assigned to a variable, the call operator or & symbol is used. The following example shows a scriptblock of {Write-Host $file.fullname $file.length} being assigned to the variable $a. After the scriptblock has been assigned to the variable $a, a foreach loop is invoked to operate against all of the *.ps1 files in the c:scripts directory. The statement list for the foreach loop is set to &$a, which executes the command {Write-Host $file.fullname $file.length} for each of the $file objects in the pipeline.

image

PS C:>  $a = {Write-Host $file.fullname $file.length}
PS C:> foreach ($file in Get-ChildItem c:scripts*.ps1) {&$a}
C:scriptsListNouns.ps1 211
C:scriptsListVerbs.ps1 211
C:scriptsmyfunction.ps1 310
C:scripts estps1.ps1 640
PS C:>

image

Another powerful use for scriptblocks is to pass them as parameters to cmdlets. In the following example, a dir command is issued with the parameter c:scripts*.ps1, which produces a list of all the *.ps1 files in c:scripts. The output of this command is pipelined to the Copy-Item cmdlet, and a scriptblock of {"C: ext" + $_.Name +".TXT"} is provided as an argument for the -Destination parameter. This enables the Copy-Item cmdlet to take each item in the pipeline, append a .TXT extension to the filename that it is passed, and then copy the resulting file to the c: ext directory.

image

PS C:> Dir c:scripts*.ps1 | Copy-Item -Destination {"C: ext" +
$_.Name + ".TXT"}
PS C:> dir c:scripts


    Directory: Microsoft.PowerShell.CoreFileSystem::C:scripts


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         6/18/2008   3:16 PM        211 ListNouns.ps1
-a---         6/18/2008   2:08 PM        211 ListVerbs.ps1
-a---         7/14/2008  11:32 AM        310 myfunction.ps1
-a---         7/14/2008  11:23 AM        640 testps1.ps1


PS C:> dir c: ext



    Directory: Microsoft.PowerShell.CoreFileSystem::C: ext


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         6/18/2008   3:16 PM        211 ListNouns.ps1.TXT
-a---         6/18/2008   2:08 PM        211 ListVerbs.ps1.TXT
-a---         7/14/2008  11:32 AM        310 myfunction.ps1.TXT
-a---         7/14/2008  11:23 AM        640 testps1.ps1.TXT

PS C:>

image

Summary

This chapter focused on the component elements of PowerShell programming and provided an overview of each of these elements. PowerShell includes a large number of expressions and operators that provide the underlying logic for almost all PowerShell operations, which were documented and demonstrated in several examples. The PowerShell escape sequences were reviewed and their usage discussed. Several different techniques for error handling in PowerShell scripts were documented, including using ubiquitous parameters, trapping errors, and the throw keyword to generate terminating errors. The construction and use of arrays in PowerShell were covered, focusing on using PowerShell’s native operators to create and manipulate one-dimensional and multidimensional arrays without the use of an array literal keyword. PowerShell’s implementation of functions was discussed in detail, including a step-by-step process to build a function that includes the same clauses as a cmdlet, in addition to storing functions in script files for later reuse. The use of filters in PowerShell was discussed in the context of providing more granular control over objects passed to a pipeline. Four different types of loops were reviewed, and the capabilities and benefits of each type of loop were enumerated. PowerShell’s mechanisms for implementing decision logic were discussed, including if/else statements, the use of labels in conjunction with the break and continue keywords, and the switch statement. Finally, the use of scriptblocks in PowerShell was reviewed, with a focus on the relationship between scriptblocks and functions, methods for assigning scriptblocks to variables and invoking scriptblocks using the call operator, and passing scriptblocks to cmdlets as arguments to enhance the capabilities of built-in PowerShell commands. Although the sheer number of commands, parameters, and options provided by the PowerShell language might be overwhelming at first glance, each of these elements is an integrated part of a logical, flexible, and extraordinarily capable shell that will quickly reward your efforts at learning and add many new possibilities to your scripting skill set.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.9.12