3.3. Store Information in
|
Variables Have Types, TooVariables provide a place to store objects and can be used to store almost any piece of data used in the command shell. Instead of relying solely on the pipeline for moving data around, we can store objects for later use.
Although it isn't necessary to say exactly what information a variable will store beforehand, as soon as a variable is assigned a value, it takes on a type. Variables are objects, too, and they can be used in the pipeline and acted upon by cmdlets such as
|
Variables in MSH are identified with the
$
prefix. This helps separate variables from cmdlets, aliases, filenames, and other identifiers used in the shell. There is no forced naming convention for variables, although it's always good practice to use a name that actually represents the information stored within; if nothing else, it can dramatically help readability and understanding of scripts later on. Variable
Let's start with some simple arithmetic and a variable called number :
MSH D:\MshScripts>
$number = 1
# create a variable and give it a value
MSH D:\MshScripts>
$number
1
MSH D:\MshScripts>
$number = 4*6
# replace the value
MSH D:\MshScripts>
$number
24
MSH D:\MshScripts>
$number += 2
# update the variable by adding two
MSH D:\MshScripts> $number
26
MSH D:\MshScripts>
$number++
# increment the variable
MSH D:\MshScripts>
$number
27
Text strings are treated in a similar fashion. Many of the same operators (such as addition and multiplication) translate directly:
MSH D:\MshScripts>
$myString = "Hello"
MSH D:\MshScripts>
$myString += ", World!"
MSH D:\MshScripts>
$myString
Hello, World!
MSH D:\MshScripts>
$myString = "Hello" * 6
MSH D:\MshScripts>
$myString
HelloHelloHelloHelloHelloHello
With the basics in place, we can look at some of the richer data structures that variables enable. Arrays give us the power to store many pieces of information within a single variable. Simple arrays contain a sequence of data, often, but not
MSH D:\MshScripts>
$arr = @(1,2,3,4)
MSH D:\MshScripts>
$arr
# list the contents, one element per line
1
2
3
4
MSH D:\MshScripts>
$arr.Count
4
MSH D:\MshScripts>
$arr[2]
# get the third element ([0] is the first)
3
MSH D:\MshScripts>
$arr = @(1,2)
MSH D:\MshScripts>
$arr += 3
MSH D:\MshScripts>
$arr
1
2
3
MSH D:\MshScripts>
$arr = @(("a", "b"), 2, 3)
MSH D:\MshScripts>
$arr[0]
a
b
Another useful data structure we'll begin to rely on is the hashtable , a special type of array where one value (the key ) is associated directly with another (the value ). Think of a hashtable like a dictionary: the key is the word you're looking up and the value is the definition that lives beside it.
Hashtables can be very useful for storing
MSH D:\MshScripts>
$h = @{}
# create a new, empty hashtable
MSH D:\MshScripts>
$h["hermes"] = "Andy"
MSH D:\MshScripts>
$h["zeus"] = "Andy"
MSH D:\MshScripts>
$h["hades"] = "John"
MSH D:\MshScripts>
$h["poseidon"] = "Bob"
MSH D:\MshScripts>
$h
Key Value
--- -----
hermes Andy
zeus Andy
hades John
poseidon Bob
MSH D:\MshScripts>
$h["zeus"]
Andy
Variables can be used to store just about any data structure used in the shell, including items that are passed through the pipeline. We can fill a variable using the same assignment operator (
=
) or by using the
MSH D:\MshScripts>
$allProcesses = get-process
MSH D:\MshScripts>
$allProcesses format-table Name
Name
----
alg
CcmExec
csrss
explorer
Idle
lsass
msh
...
MSH D:\MshScripts>
get-process where-object { $_.HandleCount -gt 200 } set-variable -Name MyProcesses
MSH D:\MshScripts>
$MyProcesses.Count
12
MSH D:\MshScripts>
$MyProcesses[0]
Handles NPM(K) PM(K) WS(K) VS(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
840 14 14572 15444 77 547.39 1656 CcmExec
One final important thing to realize is that for
value types
such as integers, strings, and arraysassignment is done
by value
. Let's look at how this workswe'll see the reasoning behind it in just a moment (as before, the 93 number returned from
MSH D:\MshScripts>
$a = 10
MSH D:\MshScripts>
$b = $a
MSH D:\MshScripts>
$b
10
MSH D:\MshScripts>
$a = 0
MSH D:\MshScripts>
$b
10
MSH D:\MshScripts>
$aliasesVar = get-alias
# $aliasesVar is an array
MSH D:\MshScripts>
$aliasesVar.Count
93
MSH D:\MshScripts>
new-alias wo where-object
# now 94 registered aliases
MSH D:\MshScripts>
$aliasesVar.Count
93
In contrast, when a reference type like a hashtable, Process object, or FileInfo object is assigned to a variable, assignment happens by reference . Again, let's first see how this works and understand the behavior in a moment:
MSH D:\MshScripts>
$ht=@{a=10;b=20}
MSH D:\MshScripts>
$ht
Key Value
--- -----
a 10
b 20
MSH D:\MshScripts>
$otherht = $ht
MSH D:\MshScripts>
$otherht["c"] = 30
MSH D:\MshScripts>
$ht
Key Value
--- -----
a 10
b 20
c 30
There are many ways to define and update variables. Basic assignments using the assignment operator (
=
) simply take the value from the
|
Assignment operator |
Equivalent to |
Effect |
|---|---|---|
|
$x += $y |
$x = $x + $y |
Add and assign |
|
$x -= $y |
$x = $x - $y |
Subtract and assign |
|
$x *= $y |
$x = $x * $y |
Multiply and assign |
|
$x /= $y |
$x = $x / $y |
Divide and assign |
The other common mechanisms for updating numeric variables are the increment and decrement operators ( $x++ and $x , respectively). Equivalent to doing $x+=1 or $x-=1 , these operators are a convenient shorthand.
Global variables, such as the ones we've seen here, are available from the moment they are defined until the shell is closed and are not
An array is index-based , which means that each element is stored in sequence and can be accessed at a numeric location. The first item in the array has an index of zero, the second an index of one, and so on. An individual element is retrieved using square brackets containing the index number and appearing immediately after the array (e.g., $arr[13] ).
Arrays can either be
$a = @(1,2,3)
$b = @(4,5,6)
$c = $a+$b # equivalent to $c=@(1,2,3,4,5,6)
$d = @(1..6) # equivalent to $d=@(1,2,3,4,5,6)
Hashtables have obvious similarities to arrays with one significant difference: instead of using a numeric index, they use an arbitrary key. For this reason, it's not possible to iterate through the contents of a hashtable using a numeric index. Thus, hashtables expose two collections Keys and Values that contain the information stored within.
Like arrays, hashtables can be built up progressively as we saw previously, populated with data at creation time, or they can have multiple key-value pairs added at once through "hashtable addition":
$ht = @{hermes="Andy"; zeus="Andy"; hades="John"; poseidon="Bob"}
$ht += @{athena="Bob"; apollo="John"}
The special value $null is used to represent a null or undefined value. The null value is very flexible. If you add a number to it, it behaves as if it were zero. If you try to append a string to it, it will behave as if it were an empty string. Add a hashtable or array to null, and it behaves as if it were an empty hashtable or array. Setting a variable to null clears any previous value it might have held.
An empty array (one that contains zero elements) is represented by the @( ) syntax. Similarly, the @{} syntax (note the curly braces) is used to create an empty hashtable.
These special values are summarized in Appendix A.
In effect, when an assignment is made with a
value type
, the variable is given a copy of its assignment at that instant. For simple variable types, such as numbers, this behavior is
In the last example, we saw that even though the third element was added to the
$otherht
hashtable, it
...What happens if you use the same variable name in two places? Will they interfere with each other? It depends. We'll come back to some more advanced aspects of variables in the
...Using
$_
as a valid variable name?
$_
is a valid variable name, although, as we saw earlier, the
$_
variable has special meaning inside certain script blocks. With some cmdlets, such as
...Using a variable before it has been assigned a value? Technically, it can be done, although it's rarely a good idea. If you use a variable that hasn't yet been assigned a value, you'll get a null value back and won't see any errors. This may seem to work out fine in some cases, but always consider whether your script will behave in the same way if the variable does have a value. In practice, it's always a good idea to set any variables to a known value (such as zero or an empty string) before using them.
...Seeing which variables you've defined? Yes. In the same way that we can browse through the registry like a filesystem, MSH creates a special drive that represents the assigned variables. You can use the standard commands to list the names and current values of any defined variables:
MSH D:\MshScripts> set-location Variable: MSH D:\MshScripts> get-childitem
...Removing elements from a hashtable? The hashtable is a rich class that exposes several
MSH D:\MshScripts>
$ht = @{a=10;b=20;c=30}
MSH D:\MshScripts>
$ht.Remove("a")
MSH D:\MshScripts>
$ht
Key Value
--- -----
b 20
c 30
MSH D:\MshScripts>
$ht.Clear( )
The language reference guides built into get-help offer a full list of arithmetic and assignment operators, as well as some more details on the various data structures available:
get-help about_Arithmetic_Operators
get-help about_Assignment_Operators
get-help about_Array