Most scripts deal with more than one thing—lists of servers, lists of files, lookup codes, and more. To enable this, PowerShell supports many features to help you through both its language features and utility cmdlets.
PowerShell makes working with arrays and lists much like working with other data types: you can easily create an array or list and then add or remove elements from it. You can just as easily sort it, search it, or combine it with another array. When you want to store a mapping between one piece of data and another, a hashtable fulfills that need perfectly.
To create an array that holds a given set of items, separate those items with commas:
PS > $myArray = 1,2,"Hello World" PS > $myArray 1 2 Hello World
To create an array of a specific size, use the
New-Object
cmdlet:
PS > $myArray = New-Object string[] 10 PS > $myArray[5] = "Hello" PS > $myArray[5] Hello
To create an array of a specific type, use a strongly typed collection:
PS > $list = New-Object Collections.Generic.List[Int] PS > $list.Add(10) PS > $list.Add("Hello") Cannot convert argument "0", with value: "Hello", for "Add" to type "System .Int32": "Cannot convert value "Hello" to type "System.Int32". Error: "Input string was not in a correct format.""
To store the output of a command that generates a list, use variable assignment:
PS > $myArray = Get-Process PS > $myArray Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 274 6 1316 3908 33 3164 alg 983 7 3636 7472 30 688 csrss 69 4 924 3332 30 0.69 2232 ctfmon 180 5 2220 6116 37 2816 dllhost (...)
To create an array that you plan to modify
frequently, use an ArrayList
, as
shown by Example 7-1.
Aside from the primitive data types (such as
strings, integers, and decimals), lists of items are a common concept in
the scripts and commands that you write. Most commands generate lists of
data: the Get-Content
cmdlet
generates a list of strings in a file, the Get-Process
cmdlet
generates a list of processes running on the system, and the Get-Command
cmdlet
generates a list of commands, just to name a few.
The solution shows how to store the output
of a command that generates a list. If a command outputs only one item
(such as a single line from a file, a single process, or a single
command), then that output is no longer a list. If you want to treat
that output as a list even when it is not, use the list evaluation
syntax ( @()
) to force PowerShell to interpret it
as an array:
$myArray = @(Get-Process Explorer)
When you want to create a list of a specific
type, the solution demonstrates how to use the
System.Collections.Generic.List
collection to do
that. After the type name, you define the type of the list in square
brackets, such as [Int]
, [String]
,
or whichever type you want to restrict your collection to. These types
of specialized objects are called generic
objects. For more information about creating generic objects, see
Creating Instances of Types.
For more information on lists and arrays in PowerShell, see Arrays and Lists.
To create an array of arrays (a
jagged array), use the @()
array
syntax:
PS > $jagged = @( (1,2,3,4), (5,6,7,8) ) PS > $jagged[0][1] 2 PS > $jagged[1][3] 8
To create a (nonjagged) multidimensional
array, use the New-Object
cmdlet:
PS > $multidimensional = New-Object "int32[,]" 2,4 PS > $multidimensional[0,1] = 2 PS > $multidimensional[1,3] = 8 PS > PS > $multidimensional[0,1] 2 PS > $multidimensional[1,3] 8
Jagged and multidimensional arrays are useful for holding lists of lists and arrays of arrays. Jagged arrays are arrays of arrays, where each array has only as many elements as it needs. A nonjagged array is more like a grid or matrix, where every array needs to be the same size. Jagged arrays are much easier to work with (and use less memory), but nonjagged multidimensional arrays are sometimes useful for dealing with large grids of data.
Since a jagged array is an array of arrays, creating an item in a jagged array follows the same rules as creating an item in a regular array. If any of the arrays are single-element arrays, use the unary comma operator. For example, to create a jagged array with one nested array of one element:
PS > $oneByOneJagged = @( ,(,1) PS > $oneByOneJagged[0][0]
For more information on lists and arrays in PowerShell, see Arrays and Lists.
To access a specific element of an array, use PowerShell’s array access mechanism:
PS > $myArray = 1,2,"Hello World" PS > $myArray[1] 2
To access a range of array elements, use array ranges and array slicing:
PS > $myArray = 1,2,"Hello World" PS > $myArray[1..2 + 0] 2 Hello World 1
PowerShell’s array access mechanisms provide a convenient way to access either specific elements of an array or more complex combinations of elements in that array. In PowerShell (as with most other scripting and programming languages), the item at index 0 represents the first item in the array.
For long lists of items, knowing the index of an element can
sometimes pose a problem. For a solution to this, see the
Add-FormatTableIndexParameter
script included with
this book’s code examples. This script adds a new
-IncludeIndex
parameter to the Format-Table
cmdlet:
PS > $items = Get-Process outlook,powershell,emacs,notepad PS > $items Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 163 6 17660 24136 576 7.63 7136 emacs 74 4 1252 6184 56 0.19 11820 notepad 3262 48 46664 88280 376 20.98 8572 OUTLOOK 285 11 31328 21952 171 613.71 4716 powershell 767 14 56568 66032 227 104.10 11368 powershell PS > $items | Format-Table -IncludeIndex PSIndex Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------- ------ ----- ----- ----- ------ -- ----------- 0 163 6 17660 24136 576 7.63 7136 emacs 1 74 4 1252 6184 56 0.19 11820 notepad 2 3262 48 46664 88280 376 20.98 8572 OUTLOOK 3 285 11 31328 21952 171 613.71 4716 powershell 4 767 14 56568 66032 227 104.15 11368 powershell PS > $items[2] Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 3262 48 46664 88280 376 20.98 8572 OUTLOOK
Although working with the elements of an array by their numerical index is helpful, you may find it useful to refer to them by something else—such as their name, or even a custom label. This type of array is known as an associative array (or hashtable). For more information about working with hashtables and associative arrays, see Create a Hashtable or Associative Array.
For more information on lists and arrays in PowerShell (including the array ranges and slicing syntax), see Arrays and Lists. For more information about obtaining the code examples for this book, see Code Examples.
To access each item in an array one by one,
use the Foreach-Object
cmdlet:
PS > $myArray = 1,2,3 PS > $sum = 0 PS > $myArray | Foreach-Object { $sum += $_ } PS > $sum 6
To access each item in an array in a more
script-like fashion, use the foreach
scripting keyword:
PS > $myArray = 1,2,3 PS > $sum = 0 PS > foreach($element in $myArray) { $sum += $element } PS > $sum 6
To access items in an array by position, use a
for
loop:
PS > $myArray = 1,2,3 PS > $sum = 0 PS > for($counter = 0; $counter -lt $myArray.Count; $counter++) { $sum += $myArray[$counter] } PS > $sum 6
PowerShell provides three main alternatives to
working with elements in an array. The Foreach-Object
cmdlet and foreach
scripting keyword techniques visit the
items in an array one element at a time, whereas the for
loop (and related looping constructs) lets
you work with the items in an array in a less structured way.
For more information about the Foreach-Object
cmdlet, see Work with Each Item in a List or Command Output.
For more information about the foreach
scripting keyword, the for
keyword, and other looping constructs, see
Repeat Operations with Loops.
To sort a list of items, use the Sort-Object
cmdlet:
PS > Get-ChildItem | Sort-Object -Descending Length | Select Name,Length Name Length ---- ------ Convert-TextObject.ps1 6868 Connect-WebService.ps1 4178 Select-FilteredObject.ps1 3252 Get-PageUrls.ps1 2878 Get-Characteristics.ps1 2515 Get-Answer.ps1 1890 New-GenericObject.ps1 1490 Invoke-CmdScript.ps1 1313
The Sort-Object
cmdlet provides a convenient way
for you to sort items by a property that you specify. If you don’t
specify a property, the Sort-Object
cmdlet follows the sorting rules of those items if they define
any.
The Sort-Object
cmdlet also
supports custom sort expressions, rather than just sorting on existing
properties. To sort by your own logic, use a script block as the sort
expression. This example sorts by the second character:
PS > "Hello","World","And","PowerShell" | Sort-Object { $_.Substring(1,1) } Hello And PowerShell World
If you want to sort a list that you’ve saved in a variable, you
can either store the results back in that variable or use the [Array]::Sort()
method from the .NET
Framework:
PS > $list = "Hello","World","And","PowerShell" PS > $list = $list | Sort-Object PS > $list And Hello PowerShell World PS > $list = "Hello","World","And","PowerShell" PS > [Array]::Sort($list) PS > $list And Hello PowerShell World
In addition to sorting by a property or expression in
ascending or descending order, the Sort-Object
cmdlet’s -Unique
switch also allows you to remove
duplicates from the sorted collection.
For more information about the Sort-Object
cmdlet, type Get-Help Sort-Object
.
To determine whether a list contains a
specific item, use the -contains
operator:
PS > "Hello","World" -contains "Hello" True PS > "Hello","World" -contains "There" False
The -contains
operator is a useful way to quickly
determine whether a list contains a specific element. To search a list
for items that instead match a pattern, use the -match
or -like
operators.
For more information about the -contains
, -match
, and -like
operators, see Comparison Operators.
To combine PowerShell arrays, use the addition
operator (+
):
PS > $firstArray = "Element 1","Element 2","Element 3","Element 4" PS > $secondArray = 1,2,3,4 PS > PS > $result = $firstArray + $secondArray PS > $result Element 1 Element 2 Element 3 Element 4 1 2 3 4
One common reason to combine two arrays is when you want to add data to the end of one of the arrays. For example:
PS > $array = 1,2 PS > $array = $array + 3,4 PS > $array 1 2 3 4
You can write this more clearly as:
PS > $array = 1,2 PS > $array += 3,4 PS > $array 1 2 3 4
When this is written in the second form, however, you might think that PowerShell simply adds the items to the end of the array while keeping the array itself intact. This is not true, since arrays in PowerShell (like most other languages) stay the same length once you create them. To combine two arrays, PowerShell creates a new array large enough to hold the contents of both arrays and then copies both arrays into the destination array.
If you plan to add and remove data from an
array frequently, the System.
Collections.
Array
List
class provides a more dynamic
alternative. For more information about using the ArrayList
class, see Use the ArrayList Class for Advanced Array Tasks.
You have an array and want to find all elements that match a given item or term—either exactly, by pattern, or by regular expression.
To find all elements that match an item, use
the -eq
, -like
, and -match
comparison
operators:
PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12" PS > $array -eq "Item 1" Item 1 Item 1 PS > $array -like "*1*" Item 1 Item 1 Item 12 PS > $array -match "Item .." Item 12
The -eq
,
-like
, and -match
operators are useful ways to find
elements in a collection that match your given term. The -eq
operator returns all elements that are
equal to your term, the -like
operator returns all elements that match the wildcard given in your
pattern, and the -match
operator
returns all elements that match the regular expression given in your
pattern.
For more complex comparison conditions, the
Where-Object
cmdlet lets you find
elements in a list that satisfy much more complex conditions:
PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12" PS > $array | Where-Object { $_.Length -gt 6 } Item 12
For more information, see Filter Items in a List or Command Output.
For more information about the -eq
, -like
,
and -match
operators, see Comparison Operators.
To compare two lists, use the Compare-Object
cmdlet:
PS > $array1 = "Item 1","Item 2","Item 3","Item 1","Item 12" PS > $array2 = "Item 1","Item 8","Item 3","Item 9","Item 12" PS > Compare-Object $array1 $array2 InputObject SideIndicator ----------- ------------- Item 8 => Item 9 => Item 2 <= Item 1 <=
The Compare-Object
cmdlet
lets you compare two lists. By default, it shows only the items that
exist exclusively in one of the lists, although its
-IncludeEqual
parameter lets you include items that
exist in both. If it returns no results, the two lists are equal.
For more information, see Chapter 22.
You want to remove all elements from an array that match a given item or term—either exactly, by pattern, or by regular expression.
To remove all elements from an array that
match a pattern, use the -ne
,
-notlike
, and -notmatch
comparison
operators, as shown in Example 7-2.
To actually remove the items from the array, store the results back in the array:
PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12" PS > $array = $array -ne "Item 1" PS > $array Item 2 Item 3 Item 12
The -eq
,
-like
, and -match
operators are useful ways to find
elements in a collection that match your given term. Their opposites,
the -ne
, -notlike
, and -notmatch
operators, return all elements that
do not match that given term.
To remove all elements from an array that match a given pattern, you can then save all elements that do not match that pattern.
For more information about the -ne
, -notlike
, and -notmatch
operators, see Comparison Operators.
To find all elements greater or less than a
given value, use the -gt
, -ge
, -lt
,
and -le
comparison operators:
PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12" PS > $array -ge "Item 3" Item 3 PS > $array -lt "Item 3" Item 1 Item 2 Item 1 Item 12
The -gt
,
-ge
, -lt
, and -le
operators are useful ways to find elements
in a collection that are greater or less than a given value. Like all
other PowerShell comparison operators, these use the comparison rules of
the items in the collection. Since the array in the solution is an array
of strings, this result can easily surprise you:
PS > $array -lt "Item 2" Item 1 Item 1 Item 12
The reason for this becomes clear when you
look at the sorted array—"Item 12"
comes before "Item 2"
alphabetically, which is the way that PowerShell
compares arrays of strings.
PS > $array | Sort-Object Item 1 Item 1 Item 12 Item 2 Item 3
For more information about the -gt
, -ge
,
-lt
, and -le
operators, see Comparison Operators.
You have an array that you want to frequently add elements to, remove elements from, search, and modify.
To work with an array frequently after you
define it, use the System.Collections.ArrayList
class:
PS > $myArray = New-Object System.Collections.ArrayList PS > [void] $myArray.Add("Hello") PS > [void] $myArray.AddRange( ("World","How","Are","You") ) PS > $myArray Hello World How Are You PS > $myArray.RemoveAt(1) PS > $myArray Hello How Are You
Like in most other languages, arrays in PowerShell stay the same length once you create them. PowerShell allows you to add items, remove items, and search for items in an array, but these operations may be time-consuming when you are dealing with large amounts of data. For example, to combine two arrays, PowerShell creates a new array large enough to hold the contents of both arrays and then copies both arrays into the destination array.
In comparison, the ArrayList
class is designed to let you easily
add, remove, and search for items in a collection.
PowerShell passes along any data that your
script generates, unless you capture it or cast it to [void]
. Since it is designed primarily to be
used from programming languages, the System.Collections.ArrayList
class produces
output, even though you may not expect it to. To prevent it from
sending data to the output pipeline, either capture the data or cast
it to [void]
:
PS > $collection = New-Object System.Collections.ArrayList PS > $collection.Add("Hello") 0 PS > [void] $collection.Add("World")
If you plan to add and remove data to and from
an array frequently, the System.
Collections.
Array
List
class provides a more dynamic
alternative.
For more information about working with classes from the .NET Framework, see Work with .NET Objects.
To define a mapping between labels and items, use a hashtable (associative array):
PS > $myHashtable = @{ Key1 = "Value1"; "Key 2" = 1,2,3 } PS > $myHashtable["New Item"] = 5 PS > PS > $myHashTable Name Value ---- ----- Key 2 {1, 2, 3} New Item 5 Key1 Value1
Hashtables are much like arrays that let you access items by whatever label you want—not just through their index in the array. Because of that freedom, they form the keystone of a huge number of scripting techniques. Since they let you map names to values, they form the natural basis for lookup tables such as those for zip codes and area codes. Since they let you map names to fully featured objects and script blocks, they can often take the place of custom objects. And since you can map rich objects to other rich objects, they can even form the basis of more advanced data structures such as caches and object graphs.
The solution demonstrates how to create and initialize a hashtable at the same time, but you can also create one and work with it incrementally:
PS > $myHashtable = @{} PS > $myHashtable["Hello"] = "World" PS > $myHashtable.AnotherHello = "AnotherWorld" PS > $myHashtable Name Value ---- ----- AnotherHello AnotherWorld Hello World
This ability to map
labels to structured values also proves helpful in interacting with
cmdlets that support advanced configuration parameters, such as the
calculated property parameters
available on the Format-Table
and
Select-Object
cmdlets. For an example
of this use, see Display the Properties of an Item as a Table.
For more information about working with hashtables, see Hashtables (Associative Arrays).
You have a hashtable of keys and values, and you want to get the list of values that result from sorting the keys in order.
To sort a hashtable, use the GetEnumerator()
method
on the hashtable to gain access to its individual elements. Then, use
the Sort-Object
cmdlet to
sort by Name
or Value
.
foreach($item in $myHashtable.GetEnumerator() | Sort Name) { $item.Value }
Since the primary focus of a hashtable is to simply map keys to values, you should not depend on it to retain any ordering whatsoever—such as the order you added the items, the sorted order of the keys, or the sorted order of the values.
This becomes clear in Example 7-3.
However, the hashtable object supports a
GetEnumerator()
method that lets you
deal with the individual hashtable entries—all of which have a Name
and Value
property. Once you have those, we can
sort by them as easily as we can sort any other PowerShell data. Example 7-4 demonstrates this
technique.
For more information about working with hashtables, see Hashtables (Associative Arrays).
3.14.144.108