• Aucun résultat trouvé

C OLLECTIONS : ARRAYS AND SEQUENCES

Dans le document Bruce PayetteSECOND EDITION (Page 122-127)

Working with types

3.4 C OLLECTIONS : ARRAYS AND SEQUENCES

In the previous section, we talked about hashtables and hash literals. Now let’s talk about the PowerShell syntax for arrays and array literals. Most programming languages have some kind of array literal notation similar to the PowerShell hash literal notation, where there’s a beginning character sequence followed by a list of values, followed by a closing character sequence. Here’s how array literals are defined in PowerShell:

They’re not. There’s no array literal notation in PowerShell.

Yes, you read that correctly. There’s no notation for an array literal in PowerShell. So how exactly does this work? How do you define an inline array in a PowerShell script?

Here’s the answer: instead of having array literals, there’s a set of operations that create collections as needed. In fact, collections of objects are created and discarded trans-parently throughout PowerShell. If you need an array, one will be created for you. If you need a singleton (or scalar) value, the collection will be unwrapped as needed.

3.4.1 Collecting pipeline output as an array

The most common operation resulting in an array in PowerShell is collecting the out-put from a pipeline. When you run a pipeline that emits a sequence of objects and assign that output to a variable, it automatically collects the elements into an array, specifically into a .NET object of type [object[]].

But what about building a simple array in an expression? The simplest way to do this is to use the comma operator (,). For example, at the command line, type

1,2,3

and you’ll have created a sequence of numbers. (See chapter 5 for more information about using the comma operator.) When you assign that sequence to a variable, it’s

stored as an array. Assign these three numbers to a variable, $a, and look at the result type:

PS (1) > $a = 1,2,3

PS (2) > $a.gettype().fullname System.Object[]

As in the pipeline case, the result is stored in an array of type [object[]]. 3.4.2 Array indexing

Let’s explore some of the operations that can be performed on arrays. As is commonly the case, getting and setting elements of the array (array indexing) is done with square brackets. The length of an array can be retrieved with the Length property:

PS (3) > $a.length 3

PS (4) > $a[0]

1

Note that arrays in PowerShell are origin-zero; that is, the first element in the array is at index 0, not index 1. As the example showed, the first element of $a is in $a[0].

As with hashtables, changes are made to an array by assigning new values to indexes in the array. The following example assigns new values to the first and third elements in $a:

PS (5) > $a[0] = 3.1415 PS (6) > $a

3.1415 2 3

PS (7) > $a[2] = "Hi there"

PS (8) > $a 3.1415 2

Hi there PS (9) >

Looking at the output, you can see that elements of the array have been changed.

Simple assignment updates the element at the specified index.

3.4.3 Polymorphism in arrays

Another important thing to note from the previous example is that arrays are poly-morphic by default. By polypoly-morphic we mean that you can store any type of object in an array. (A VBScript user would call these variant arrays.) When you created the array, you assigned only integers to it. In the subsequent examples, you assigned a floating-point number and a string. The original array was capable of storing any kind of object. In formal terms, PowerShell arrays are polymorphic by default (though it’s possible to create type-constrained arrays).

COLLECTIONS: ARRAYSANDSEQUENCES 93 Earlier you saw how to get the length of an array. What happens when you try to assign to an element past the end of the array? The next example illustrates this:

PS (9) > $a.length 3

PS (10) > $a[4] = 22

Array assignment failed because index '4' was out of range.

At line:1 char:4 + $a[4 <<<< ] = 22 PS (11) >

Attempts to assign outside the bounds of an array will result in a range error. This is because PowerShell arrays are based on .NET arrays and their size is fixed. So how can you add more elements to a PowerShell array if the underlying objects are fixed in size? This is easily done through array concatenation using the plus (+) or plus-equals (+=) operators. Let’s add two more elements to the array from the previous example:

PS (11) > $a += 22,33 PS (12) > $a.length 5

PS (13) > $a[4]

33 PS (14) >

So the length of the array in $a is now 5. The addition operation did add elements.

Here’s how this works:

1 PowerShell creates a new array large enough to hold the total number of elements.

2 It copies the contents of the original array into the new one.

3 It copies the new elements into the end of the array.

You didn’t add any elements to the original array after all. Instead, you created a new, larger one.

3.4.4 Arrays as reference types

This copying behavior has some interesting consequences. You can explore this fur-ther by first creating a simple array and looking at the value. Let’s use string expan-sion here so that the values in the variable are all displayed on one line:

PS (1) > $a=1,2,3 PS (2) > "$a"

1 2 3

Now assign $a to a new variable, $b, and check that $a and $b have the same elements:

PS (3) > $b = $a PS (4) > "$b"

1 2 3

Next, change the first element in $a:

PS (5) > $a[0] = "Changed"

PS (6) > "$a"

Changed 2 3

Yes, the first element in $a was changed. But what about $b?

PS (7) > "$b"

Changed 2 3

It was also changed. As with hashtables, array assignment is done by reference. When you assigned $a to $b, you got a copy of the reference to the array instead of a copy of contents of the array. Add a new element to $b:

PS (8) > $b += 4 PS (9) > "$b"

Changed 2 3 4

$b is now four elements long. Because of the way array concatenation works, $b con-tains a copy of the contents of the array instead of a reference. If you change $a now, it won’t affect $b. Let’s verify that:

PS (10) > $a[0] = "Changed again"

PS (11) > "$a"

Changed again 2 3 PS (12) > "$b"

Changed 2 3 4

You see that $b wasn’t changed. Conversely, changing $b should have no effect on $a:

PS (13) > $b[0] = 1 PS (14) > "$a"; "$b"

Changed again 2 3 1 2 3 4

PS (15) >

Again, there was no change.

To reiterate, arrays in PowerShell, like arrays in other .NET languages, are refer-ence types, not value types. When you assign them to a variable, you get another reference to the array, not another copy of the array.

3.4.5 Singleton arrays and empty arrays

You saw how to use the comma operator to build up an array containing more than one element. You can also use the comma operator as a prefix operator to create an array containing only one element. The next example shows this:

PS (1) > , 1 1

PS (2) > (, 1).length 1

PS (3) >

This code creates an array containing a single element, 1.

COLLECTIONS: ARRAYSANDSEQUENCES 95 How about empty arrays? The comma operator always takes an argument to work on. Even using $null as an argument to the comma operator will result in a one-element array containing the $null reference. Empty arrays are created through a special form of subexpression notation that uses the @ symbol instead of the $ sign to start the expression. Here’s what it looks like:

PS (3) > @()

PS (4) > @().length 0

PS (5) >

In the preceding example, you created an array of length 0. This notation is more general—it takes the result of the expression it encloses and ensures that it’s always returned as an array. If the expression returns $null or a scalar value, it will be wrapped in a one-element array. Given this behavior, the other solution to creating an array with one element is

PS (1) > @(1) 1

PS (2) > @(1).length 1

That is, you place the value you want in the array in @( ... ) and you get an array back.

Use this notation when you don’t know whether the command you’re calling is going to return an array. By executing the command in this way, you’re guaranteed to get an array back. Note that if what you’re returning is already an array, it won’t be wrapped in a new array. Compare this to the use of the comma operator:

PS (1) > 1,2,3 1

2 3

PS (2) > (1,2,3).Length 3

PS (3) > ( , (1,2,3) ).Length 1

PS (4) > ( @( 1,2,3 ) ).Length 3

Line 1 created a regular array; on line 5, you get the length and see that it’s 3. Next, on line 7, you apply the prefix operator to the array and then get the length. The result now is only 1. This is because the unary comma operator always wraps its argu-ments in a new array. Finally, on line 9, you use the @( ... ) notation and then get the length. This time it remains 3. The @( ... ) sequence doesn’t wrap unless the object isn’t an array.

Now let’s look at the last type of literal: the type literal. Because object types are so important in PowerShell, you need to be able to express types in a script. Remember with numbers, when you wanted to say, “Get me all the files larger than 10 MB,” you

needed numeric literals? The same concept applies to types. If you want to be able to say, “Get me all the objects of a particular type,” you need to be able to express that type in the script.

Dans le document Bruce PayetteSECOND EDITION (Page 122-127)