• Aucun résultat trouvé

G ROUPING AND SUBEXPRESSIONS

Dans le document Bruce PayetteSECOND EDITION (Page 188-193)

Advanced operators and variables

5.3 G ROUPING AND SUBEXPRESSIONS

So far you’ve seen a variety of situations where collections of expressions or statements have been grouped together. You’ve even used these grouping constructs in string expansions. These operators are shown in figure 5.4.

Now let’s look at them in more detail. Table 5.3 provides more details and some examples.

( <pipeline>) $( <statementList> ) @( <statementList> ) Grouping, expression, and subexpression operators

Figure 5.4 The PowerShell operators for grouping expressions and statements

Table 5.3 Expression and statement grouping operators

Operator Example Results Description either a simple expression or a simple pipeline. They may not contain more than one statement or things like while loops. collec-tions of statements as opposed to being limited to a single expression. If the contained statements return a single value, that value will be returned as a scalar. If the statements return more than one value, they will be accumulated in an array.

The first grouping notation is the simple parenthetical notation. As in most lan-guages, the conventional use for this notation is to control the order of operations, as shown by the following example: per-formed first. In PowerShell, parentheses also have another use. Looking at the syntax specification shown in figure 5.4 for parenthetical expressions illustrates this:

( <pipeline> )

From the syntax, you can see that pipelines are allowed between simple parentheses.

This allows you to use a command or pipeline as a value in an expression. For exam-ple, to obtain a count of the number of files in a directory, you can use the dir com-mand in parentheses, then use the Count property to get the number of objects returned:

PS (1) > (dir).count 46

Using a pipeline in the parentheses lets you get a count of the number of files match-ing the wildcard pattern *.doc:

PS (2) > (dir | where {$_.name -like '*.doc'}).count 32

NOTE People familiar with other languages tend to assume that the expression (1,2,3,4) is an array literal in PowerShell. In fact, as you learned in chapter 3, this isn’t the case. The comma operator, discussed in the next section, allows you to easily construct arrays in PowerShell, but there are no array literals as such in the language. All that the parentheses do is control the order of operations. Otherwise, there’s nothing special about them. In fact, the precedence of the comma operator is such that you typically never need parentheses for this pur-pose. More on that later. in the same manner as the regu-lar subexpression operator, but with the additional behavior that the result will always be returned as an array.

Table 5.3 Expression and statement grouping operators (continued)

Operator Example Results Description

GROUPINGANDSUBEXPRESSIONS 159 Now let’s move on to the next set of grouping constructs—the subexpressions. There are two forms of the subexpression construct, as shown in the following:

$( <statementList> )

@( <statementList> )

5.3.1 Subexpressions $( ... )

The syntactic difference between a subexpression (either form) and a simple paren-thetical expression is that you can have any list of statements in a subexpression instead of being restricted to a single pipeline. This means that you can have any PowerShell language element in these grouping constructs, including loop state-ments. It also means that you can have several statements in the group. Let’s look at an example. Earlier in this chapter, you saw a short piece of PowerShell code that cal-culates the numbers in the Fibonacci sequence below 100. At the time, you didn’t count the number of elements in that sequence. You can do this easily using the sub-expression grouping construct:

PS (1) > $($c=$p=1; while ($c -lt 100) {$c; $c,$p=($c+$p),$c}).count 10

By enclosing the statements in $( ... ), you can retrieve the result of the enclosed collection of statements as an array.

NOTE Many languages have a special notation for generating collec-tions of objects. For example, Python and functional languages such as Haskell have a feature called list comprehensions for doing this. Power-Shell (and shell languages in general) don’t need special syntax for this kind of operation. Collections occur naturally as a consequence of the shell pipeline model. If a set of statements used as a value returns mul-tiple objects, they’ll automatically be collected into an array.

Another difference between the subexpression construct and simple parentheses is how voidable statements are treated. We looked at this concept earlier with the increment and decrement operators. A voidable expression is one whose result is discarded when used directly as a statement. Here’s an example that illustrates this. First initialize $a to 0; then use a postincrement expression in parentheses and assign it to the variable $x:

PS (1) > $a=0 PS (2) > $x=($a++)

Checking the value of $x, you see that it is 0, as expected, and that $a is now 1:

PS (3) > $x 0

PS (4) > $a 1

Now do a second assignment, this time with the expression in $( ... ):

PS (5) > $x=$($a++)

Checking the value, you see that it’s actually $null:

PS (6) > $x

PS (7) > $x -eq $null True

This is because the result of the postincrement operation was discarded, so the expres-sion returned nothing. Try a more complex statement in the subexpresexpres-sion:

PS (8) > $x=$($a++;$a;$a++;$a) PS (9) > $x

3 4

Notice that even though there are four statements in the subexpression, $x only received two values. Again, the results of the postincrement statements were discarded so they don’t appear in the output.

Next, let’s take a look at the difference between the array subexpression @( ... ) and the regular subexpression.

5.3.2 Array subexpressions @( ... )

The difference is that in the case of the array subexpression, the result is always returned as an array; this is a fairly small but useful difference. In effect, it’s shorthand for

[object[]] $( ... )

This shorthand exists because in many cases you don’t know if a pipeline operation is going to return a single element or a collection. Rather than writing complex checks, you can use this construction and be assured that the result will always be a collec-tion. If the pipeline returns an array, no new array is created and the original value is returned as is. If the pipeline returns a scalar value, that value will be wrapped in a new one-element array. It’s important to understand how this is different from the behavior of the comma operator, which always wraps its argument value in a new one-element array. Doing something like @( @(1) ) doesn’t give you a one-element array containing a second one-element array containing a number. These expressions

@(1)

@(@(1))

@(@(@(1)))

all return the same value. On the other hand,

,1

nests to one level,

,,1

nests to two levels, and so forth.

NOTE How to figure out what the pipeline returns is the single hard-est thing to explain in the PowerShell language. The problem is that

GROUPINGANDSUBEXPRESSIONS 161 people get confused; they see that @(12) returns a one-element array containing the number 12. Because of prior experience with other lan-guages, they expect that @(@(12)) should therefore produce a nested array, an array of one element containing an array of one element, which is the integer 12. As mentioned previously, this is not the case.

@(@(12)) returns exactly the same thing as @(12). If you think of rewriting this expression as [object[]]$([object[]] $( 12 )), then it’s clear why this is the case—casting an array into an array of the same type has no effect; it’s already the correct type, so you just get the original array.

Here’s an example of where this feature is useful: a pipeline expression that sorts some strings, then returns the first element in the sorted collection. Start by sorting an array of three elements:

PS (1) > $("bbb","aaa","ccc" | sort )[0]

aaa

This returns “aaa,” as you expect. Now do it with two elements:

PS (2) > $("bbb","aaa" | sort )[0]

aaa

Still “aaa,” so everything makes sense. Now try it with one element:

PS (3) > $("aaa" | sort )[0]

a

Wait a minute—what happened here? Well, what happened is that you sorted one ele-ment, and in a pipeline, you can’t tell if the commands in the pipeline mean to return a single object (a scalar) or an array containing a single object. The default behavior in PowerShell is to assume that if you return one element, you intended to return a scalar.

In this case, the scalar is the string “aaa” and index 0 of this array is the letter a, which is what the example returns. This is where you use the array subexpression notation because it ensures that you always get what you want. you know you want the pipeline to return an array, and by using this notation, you can enforce the correct behavior.

Here are the same three examples again, but this time using the array subexpression:

PS (4) > @("bbb","aaa","ccc" | sort )[0]

aaa

PS (5) > @("bbb","aaa" | sort )[0]

aaa

PS (6) > @("aaa" | sort )[0]

aaa PS (7) >

This time, all three commands return “aaa” as intended. So why have this notation?

Why not just use the casts? Well, here’s what it looks like using the cast notation:

PS (7) > ( [object[]] ("aaa" | sort ))[0]

aaa

Because of the way precedence works, you need an extra set of parentheses to get the ordering right, which makes the whole expression harder to write. In the end, the array subexpression notation is easy to use, although it’s a bit difficult to grasp at first.

The advantage is that you only have to learn something once, but you have to use it over and over again.

Now let’s move on to the other operations PowerShell provides for dealing with collections and arrays of objects. The ability to manipulate collections of objects effectively is the heart of any automation system. You can easily perform a single operation manually, but the problem is performing operations on a large set of objects. Let’s see what PowerShell has to offer here.

Dans le document Bruce PayetteSECOND EDITION (Page 188-193)