Chapter 3. Variables

by Mar 11, 2012

It is time to combine commands whenever a single PowerShell command can't solve your problem. One way of doing this is by using variables. PowerShell can store results of one command in a variable and then pass the variable to another command. In this chapter, we'll explain what variables are and how you can use them to solve more complex problems.

Topics Covered:

Personal Variables

Variables store pieces of information. This way, you can first gather all the information you may need and store them in variables. The following example stores two pieces of information in variables and then calculates a new result:

# Create variables and assign to values
$amount = 120
$VAT = 0.19

# Calculate:
$result = $amount * $VAT

# Output result
$result

22.8
# Replace variables in text with values:
$text = "Net amount $amount matches gross amount $result"
$text
Net amount 120 matches gross amount 142.8

Of course, you can have hard-coded the numbers you multiplied. However, variables are the prerequisite for reusable code. By assigning your data to variables, you can easily change the information, either by manually assigning different values to your variables or by assigning user-defined values to your variables. By simply replacing the first two lines, your script can interactively ask for the variable content:

[Int]$amount = "Enter amount of money"
[Double]$VAT = "Enter VAT rate"

Note that I strongly-typed the variables in this example. You will hear more about variable typing later in that character , but whenever you use Read-Host or another method that accepts user input, you have to specify the variable data type or else PowerShell will treat your input as simple string. Simple text is something very different from numbers and you cannot calculate with pieces of text.

PowerShell creates new variables automatically so there is no need to specifically "declare" variables. Simply assign data to a variable. The only thing you do need to know is that variable names are always prefixed with a "$" to access the variable content.

You can then output the variable content by entering the variable name or you can merge the variable content into strings. Just make sure to use double-quotes to do that. Single-quoted text will not expand variable values.

Selecting Variable Names

You are free to call the variable anything you like – as long as the name is not causing misunderstandings. Variable names are always case-insensitive.

There are some special characters that have special meaning to PowerShell. If you used those in your variable names, PowerShell can get confused. So the best thing is to first avoid special characters in your variable names. But if you must use them for any reason, be sure to enclose the variable name in brackets:

${#this is a strange variable name} = 12
${#this is a strange variable name}
12

Assigning and Returning Values

The assignment operator "=" assigns a value to a variable. You can assign almost anything to a variable, even complete command results:

# Temporarily store results of a cmdlet:
$listing = Get-ChildItem c:
$listing
Directory: Microsoft.PowerShell.CoreFileSystem::C:

Mode LastWriteTime Length Name
(…)

# Temporarily store the result of a legacy external command:
$result = ipconfig
$result
Windows IP Configuration

Ethernet adapter LAN Connection:

Media state

. . . . . . . . . . . : Medium disconnected
Connection-specific DNS Suffix:
Ethernet adapter LAN Connection 2:

Media state

. . . . . . . . . . . : Medium disconnected
Connection-specific DNS Suffix:
Wireless LAN adapter wireless network connection:

Media state

. . . . . . . . . . . : Medium disconnected
Connection-specific DNS Suffix:

Assigning Multiple Variable Values

If you'd like, you can use the assignment operator to assign values to multiple variables at the same time:

# Populate several variables with the same value in one step:
$a = $b = $c = 1
$a
1
$b
1
$c
1

Exchanging the Contents of Variables

Now and then you might want to exchange the contents of two variables. In traditional programming languages, that would require several steps:

$Value1 = 10
$Value2 = 20
$Temp = $Value1
$Value1 = $Value2
$Value2 = $Temp

With PowerShell, swapping variable content is much easier because you can assign multiple values to multiple variables. Have a look:

# Exchange variable values:
$Value1 = 10; $Value2 = 20
$Value1, $Value2 = $Value2, $Value1

Assigning Different Values to Several Variables

When you swap variable content like in the past example, it is possible because of arrays. The comma is used to create arrays, which are basically a list of values. If you assign one list of values to another list of values, PowerShell can assign multiple values at the same time. Have a look:

# Populate several variables with the same value in one step:
$Value1, $Value2 = 10,20
$Value1, $Value2 = $Value2, $Value1

Listing Variables

PowerShell keeps a record of all variables, which is accessible via a virtual drive called variable:. Here is how you see all currently defined variables:

Dir variable:

Aside from your own personal variables, you'll see many more. PowerShell also defines variables and calls them "automatic variables." You'll learn more about this soon.

Finding Variables

Using the variable: virtual drive can help you find variables. If you'd like to see all the variables containing the word "Maximum," try this:

Dir variable:*maximum*
Name                   Value
—-                   —–
MaximumErrorCount      256
MaximumVariableCount   4096
MaximumFunctionCount   4096
MaximumAliasCount      4096
MaximumDriveCount      4096
MaximumHistoryCount    1000

The solution isn't quite so simple if you'd like to know which variables currently contain the value 20. It consists of several commands piped together.

dir variable: | Out-String -stream | Select-String " 20 "
value2 20
$ 20

Here, the output from Dir is passed on to Out-String, which converts the results of Dir into string. The parameter -Stream ensures that every variable supplied by Dir is separately output as string. Select-String selects the lines that include the desired value, filtering out the rest. White space is added before and after the number 20 to ensure that only the desired value is found and not other values that contain the number 20 (like 200).

Verify Whether a Variable Exists

Using the cmdlet Test-Path, you can verify whether a certain file exists. Similar to files, variables are stored in their own "drive" called variable: and every variable has a path name that you can verify with Test-Path. You can use this technique to find out whether you are running PowerShell v1 or v2:

# Verify whether the variable $psversiontable exists which is present only in PS v2:
Test-Path variable:psversiontable
True
# Use this information to check for PS v2
If (Test-Path variable:psversiontable) {
'You are running PowerShell v2'
} else {
'You are running PowerShell v1 and should update to v2'
}
False

Deleting Variables

PowerShell will keep track of variable use and remove variables that are no longer used so there is no need for you to remove variables manually. If you'd like to delete a variable immediately, again, do exactly as you would in the file system:

# create a test variable:
$test = 1

# verify that the variable exists:
Dir variable:te*

# delete variable:
del variable:test

# variable is removed from the listing:
Dir variable:te*

Using Special Variable Cmdlets

To manage your variables, PowerShell provides you with the five separate cmdlets listed in Table 3.1. Two of the five cmdlets offer substantially new options:

  1. New-Variable enables you to specify options, such as a description or write protection. This makes a variable into a constant. Set-Variable does the same for existing variables.
  2. Get-Variable enables you to retrieve the internal PowerShell variables store.

Cmdlet Description Example
Clear-Variable Clears the contents of a variable, but not the variable itself. The subsequent value of the variable is NULL (empty). If a data or object type is specified for the variable, by using Clear-Variable the type of the objected stored in the variable will be preserved. Clear-Variable a
same as: $a = $null
Get-Variable Gets the variable object, not the value in which the variable is stored. Get-Variable a
New-Variable Creates a new variable and can set special variable options. New-Variable value 12
Remove-Variable Deletes the variable, and its contents, as long as the variable is not a constant or is created by the system. Remove-Variable a
same as: del variable:a
Set-Variable Resets the value of variable or variable options, such as a description and creates a variable if it does not exist. Set-Variable a 12
same as: $a = 12

Table 3.1: Cmdlets for managing variables

Write-Protecting Variables: Creating Constants

Constants store a constant value that cannot be modified. They work like variables with a write-protection.

PowerShell doesn't distinguish between variables and constants. However, it does offer you the option of write-protecting a variable. In the following example, the write-protected variable $test is created with a fixed value of 100. In addition, a description is attached to the variable.

# Create new variable with description and write-protection:
New-Variable test -value 100 -description `
"test variable with write-protection" -option ReadOnly
$test
100
# Variable contents cannot be modified:
$test = 200
The variable "test" cannot be overwritten since it is a
constant or read-only.
At line:1 char:6
+ $test <<<< = 200

The variable is now write-protected and its value may no longer be changed. You'll receive an error message if you try it anyway. Because the variable is write-protected, it behaves like a read-only file. You'll have to specify the parameter -Force to delete it:

del variable:test -force
$test = 200

As you just saw, a write-protected variable can still be modified by deleting it and creating a new copy of it. If you need stronger protection, you can create a variable with the Constant option. Now, it can neither be modified nor deleted. Only when you quit PowerShell are constants removed. Variables with the Constant option may only be created with New-Variable. If a variable already exists, you cannot make it constant anymore because you’ll get an error message:

#New-Variable cannot write over existing variables:
New-Variable test -value 100 -description `
"test variable with copy protection" -option Constant
New-Variable : A variable named "test" already exists.
At line:1 Char:13
+ New-Variable <<<< test -value 100 -description
"test variable with copy protection" -option Constant
# If existing variable is deleted, New-Variable can create
# a new one with the "Constant" option:
del variable:test -force
New-Variable test -value 100 -description `
"test variable with copy protection" `
-option Constant

# variables with the "Constant" option may neither be
# modified nor deleted:
del variable:test -force

Remove-Item : variable "test" may not be removed since it is a
constant or write-protected. If the variable is write-protected,
carry out the process with the Force parameter.
At line:1 Char:4
+ del <<<< variable:test -force

You can overwrite an existing variable by using the -Force parameter of New-Variable if the existing variable wasn't created with the Constant option. Variables of the constant type are unchangeable once they have been created and -Force does not change this:

# Parameter -force overwrites existing variables if these do not
# use the "Constant" option:
New-Variable test -value 100 -description "test variable" -force
New-Variable : variable "test" may not be removed since it is a
constant or write-protected.
At line:1 char:13
+ New-Variable <<<< test -value 100 -description "test variable"
# normal variables may be overwritten with -force without difficulty.
$available = 123
New-Variable available -value 100 -description "test variable" -force

Variables with Description

Variables can have an optional description to help you keep track of what the variable was intended for. However, this description appears to be invisible:

# Create variable with description:
New-Variable myvariable -value 100 -description "test variable" -force

# Variable returns only the value:
$myvariable

100
# Dir and Get-Variable also do not deliver the description:
Dir variable:myvariable
Name       Value
—-       —–
myvariable 100
Get-Variable myvariable
Name       Value
—-       —–
myvariable 100

"Automatic" PowerShell Variables

PowerShell also uses variables for internal purposes and calls those "automatic variables.” These variables are available right after you start PowerShell since PowerShell has defined them during launch. The drive variable: provides you with an overview of all variables:

Get-Childitem variable:
Name            Value
—-            —–
Error           {}
DebugPreference SilentlyContinue
PROFILE         C:UsersTobias WeltnerDocumentsWindowsPowerShellMicro…
HOME            C:UsersTobias Weltner
(…)

You can show their description to understand the purpose of automatic variables:

Get-Childitem variable: | Sort-Object Name |
Format-Table Name, Description -AutoSize -Wrap

Use Get-Help to find out more

PowerShell write protects several of its automatic variables. While you can read them, you can't modify them. This makes sense because information, like the process-ID of the PowerShell console or the root directory, must not be modified.

$pid = 12
Cannot overwrite variable "PID" because it is read-only or constant.
At line:1 char:5
+ $pid <<<< = 12

A little later in this chapter, you'll find out more about how write-protection works. You'll then be able to turn write-protection on and off for variables that already exist. However, don't do this for automatic variables because PowerShell may crash. One reason is because PowerShell continually modifies some variables. If you set them to read-only, PowerShell may stop and not respond to any inputs.

Environment Variables

There is another set of variables maintained by the operating system: environment variables.

Working with environment variables in PowerShell is just as easy as working with internal PowerShell variables. All you need to do is add the prefix to the variable name: env:.

Reading Environment Variables

You can read the location of the Windows folder of the current computer from a Windows environment variable:

$env:windir
C:Windows

By adding env:, you’ve told PowerShell not to look for the variable windir in the default PowerShell variable store, but in Windows environment variables. In other word, the variable behaves just like any other PowerShell variable. For example, you can embed it in some text:

"The Windows folder is here: $env:windir"
The Windows folder is here: C:Windows

You can just as easily use the variable with commands and switch over temporarily to the Windows folder like this:

# save in current folder:
Push-Location

# change to Windows folder
cd $env:windir
Dir

# change back to initial location after executed task
Pop-Location

Searching for Environment Variables

PowerShell keeps track of Windows environment variables and lists them in the env: virtual drive. So, if you'd like an overview of all existing environment variables, you can list the contents of the env: drive:

Get-Childitem env:
Name            Value
—-            —–
Path            C:Windowssystem32;C:Windows;C:WindowsSystem32Wbem;C:
TEMP            C:UsersTOBIAS~1AppDataLocalTemp
ProgramData     C:ProgramData
PATHEXT         .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.4mm
ALLUSERSPROFILE C:ProgramData
PUBLIC          C:UsersPublic
OS              Windows_NT
USERPROFILE     C:UsersTobias Weltner
HOMEDRIVE       C:
(…)

You’ll be able to retrieve the information it contains when you’ve located the appropriate environment variable and you know its name:

$env:userprofile
C:UsersTobias Weltner

Modifying Environment Variables

You can modify environment variables by simply assigning new variables to them. Modifying environment variables can be useful to change the way your machine acts. For example, all programs and scripts located in a folder that is listed in the "PATH" environment variable can be launched by simply submitting the file name. You no longer need to specify the complete path or a file extension.

The next example shows how you can create a new folder and add it to the PATH environment variable. Any script you place into that folder will then be accessible simply by entering its name:

# Create a special folder:
md c:myTools

# Create and example script in this folder:
" 'Hello!' " > c:myToolssayHello.ps1

# Typically, you would have to specify a qualified path name:
C:myToolssayHello.ps1
Hello!

# The folder is now added to the path environment:
$env:path += ";C:myTools"

# All scripts and commands in this folder can be launched by entering their name now:
sayHello
Hello!

Permanent Modifications of Environment Variables

By default, PowerShell works with the so-called "process" set of environment variables. They are just a copy and only valid inside your current PowerShell session (and any programs you launch from it). Changes to these environment variables will not persist and are discarded once you close your PowerShell session.

You have two choices if you need to make permanent changes to your environment variables. You can either make the changes in one of your profile scripts, which get executed each time you launch PowerShell (then your changes are effective in any PowerShell session but not outside) or you can use sophisticated .NET methods directly to change the underlying original environment variables (in which case the environment variable change is visible to anyone, not just PowerShell sessions). This code adds a path to the Path environment variable and the change is permanent.

$oldValue = [environment]::GetEnvironmentvariable("Path", "User")
$newValue = ";c:myTools"
[environment]::SetEnvironmentvariable("Path", $newValue, "User")

Access to commands of the .NET Framework as shown in this example will be described in depth in Chapter 6

When you close and restart PowerShell, the Path environment variable will now retain the changed value. You can easily check this:

$env:Path

The permanent change you just made applies only to you, the logged-on user. If you’d like this change to be in effect for all computer users, you can replace the "User" argument by "Machine." You will need full administrator privileges to do that.

You should only change environment variables permanently when there is no other way. For most purposes, it is completely sufficient to change the temporary process set from within PowerShell. You can assign it the value of $null to remove a value.

Scope of Variables

PowerShell variables can have a "scope," which determines where a variable is available. PowerShell supports four special variable scopes: global, local, private, and script. These scopes allow you to restrict variable visibility in functions or scripts.

Automatic Restriction

Typically, a script will use its own variable scope and isolate all of its variables from the console. So when you run a script to do some task, it will not leave behind any variables or functions defined by that script once the script is done.

Changing Variable Visibility

You can change this default behavior in two different ways. One is to call the script "dot-sourced": type in a dot, then a space, and then the path to the script. Now, the script’s own scope is merged into the console scope. Every top-level variables and functions defined in the script will behave as if they had been defined right in the console. So when the script is done, it will leave behind all such variables and functions.

Dot-sourcing is used when you want to (a) debug a script and examine its variables and functions after the script ran, and (b) for library scripts whose purpose is to define functions and variables for later use. The profile script, which launches automatically when PowerShell starts, is an example of a script that always runs dot-sourced. Any function you define in any of your profile scripts will be accessible in your entire PowerShell session – even though the profile script is no longer running.

Setting Scope

While the user of a script can somewhat control scope by using dot-sourcing, a script developer has even more control over scope by prefixing variable and function names. Let's use the scope modifiers private, local, script, and global.

Scope allocation Description
$private:test = 1 The variable exists only in the current scope. It cannot be accessed in any other scope.
$local:test = 1 Variables will be created only in the local scope. That's the default for variables that are specified without a scope. Local variables can be read from scopes originating from the current scope, but they cannot be modified.
$script:test = 1 This scope represents the top-level scope in a script. All functions and parts of a script can share variables by addressing this scope.
$global:test = 1 This scope represents the scope of the PowerShell console. So if a variable is defined in this scope, it will still exist even when the script that is defining it is no longer running.

Table 3.3: Variable scopes and validity of variables

Script blocks represent scopes in which variables and functions can live. The PowerShell console is the basic scope (global scope). Each script launched from the console creates its own scope (script scope) unless the script is launched "dot-sourced." In this case, the script scope will merge with the caller’s scope.

Functions again create their own scope and functions defined inside of other functions create additional sub-scopes.

Here is a little walk-through. Inside the console, all scopes are the same, so prefixing a variable will not make much difference:

$test = 1
$local:test
1
$script:test = 12
$global:test
12
$private:test
12

Differences become evident only once you create additional scopes, such as by defining a function:

# Define test function:
Function test { "variable = $a"$a = 1000 }

# Create variable in console scope and call test function:
$a = 12
Test

variable = 12
# After calling test function, control modifications in console scope:
$a
12

When you don't use any special scope prefix, a child scope can read the variables of the parent scope, but not change them. If the child scope modifies a variable that was present in the parent scope, as in the example above, then the child scope actually creates a completely new variable in its own scope, and the parent scope's variable remains unchanged.

There are exceptions to this rule. If a parent scope declares a variable as "private," then it is accessible only in that scope and child scopes will not see the variable.

# Define test function:
Function test { "variable = $a"$a = 1000 }

# Create variable in console scope and call test function:
$private:a = 12
Test

variable =
# Check variable for modifications after calling test function in console scope:
$a
12

Only when you create a completely new variable by using $private: is it in fact private. If the variable already existed, PowerShell will not reset the scope. To change scope of an existing variable, you will need to first remove it and then recreate it: Remove-Variable a would remove the variable $a. Or, you can manually change the variable options: (Get-Variable a).Options = "Private." You can change a variable scope back to the initial default "local” by assigning (Get-Variable a).Options = "None."

Variable Types and "Strongly Typing"

Variables by default are not restricted to a specific data type. Instead, when you store data in a variable, PowerShell will automatically pick a suitable data type for you. To find out what data types really are, you can explore data types. Call the method GetType(). It will tell you the data type PowerShell has picked to represent the data:

(12).GetType().Name
Int32
(1000000000000).GetType().Name
Int64
(12.5).GetType().Name
Double
(12d).GetType().Name
Decimal
("H").GetType().Name
String
(Get-Date).GetType().Name
DateTime

PowerShell will by default use primitive data types to store information. If a number is too large for a 32-bit integer, it switches to 64-bit integer. If it's a decimal number, then the Double data type best represents the data. For text information, PowerShell uses the String data type. Date and time values are stored in DateTime objects.

This process of automatic selection is called "weak typing," and while easy, it's also often restrictive or risky. Weakly typed variables will happily accept anything, even wrong pieces of information. You can guarantee that the variable gets the information you expected by strongly typing a variable — or else will throw an exception that can alarm you.

Also, PowerShell will not always pick the best data type. Whenever you specify text, PowerShell will stick to the generic string type. If the text you specified was really a date or an IP address, then there are better data types that will much better represent dates or IP addresses.

So, in practice, there are two important reasons for you to choose the data type yourself:

  • Type safety: If you have assigned a type to a variable yourself, then the type will be preserved no matter what and will never be automatically changed to another data type. You can be absolutely sure that a value of the correct type is stored in the variable. If someone later on wants to mistakenly assign a value to the variable that doesn't match the originally chosen type, this will cause an exception.
  • Special variable types: When automatically assigning a variable type, PowerShell will choose from generic variable types like Int32 or String. Often, it's much better to store values in a specialized and more meaningful variable type like DateTime.

Strongly Typing

You can enclose the type name in square brackets before the variable name to assign a particular type to a variable. For example, if you know that a particular variable will hold only numbers in the range 0 to 255, you can use the Byte type:

[Byte]$flag = 12
$flag.GetType().Name
Byte

The variable will now store your contents in a single byte, which is not only very memory-efficient, but it will also raise an error if a value outside the range is specified:

$flag = 300
The value "300" cannot be converted to the type "System.Byte".
Error: "The value for an unsigned byte was too large or too small."
At line:1 char:6
+ $flag <<<< = 300

The Advantages of Specialized Types

If you store a date as String, you'll have no access to special date functions. Only DateTime objects offer all kinds of methods to deal with date and time information. So, if you're working with date and time information, it's better to store it explicitly as DateTime:

$date = "November 12, 2004"
$date
November 12, 2004

If you store a date as String, then you'll have no access to special date functions. Only DateTime objects make them available. So, if you're working with date and time indicators, it's better to store them explicitly as DateTime:

[datetime]$date = "November 12, 2004"
$date
Friday, November 12, 2004 00:00:00

Now, since the variable converted the text information into a specific DateTime object, it tells you the day of the week and also enables specific date and time methods. For example, a DateTime object can easily add and subtract days from a given date. This will get you the date 60 days from the date you specified:

$date.AddDays(60)
Tuesday, January 11, 2005 00:00:00

PowerShell supports all.NET data types. XML documents will be much better represented using the XML data type then the standard String data type:

# PowerShell stores a text in XML format as a string:
$t = "<servers><server name='PC1' ip='10.10.10.10'/>" +
"<server name='PC2' ip='10.10.10.12'/></servers>"
$t
<servers><server name='PC1' ip='10.10.10.10'/>
<server name='PC2' ip='10.10.10.12'/></servers>
# If you assign the text to a data type[xml], you'll
# suddenly be able to access the XML structure:
[xml]$list = $t
$list.servers
server
——
{PC1, PC2}
$list.servers.server
name ip
—- —
PC1  10.10.10.10
PC2  10.10.10.12
# Even changes to the XML contents are possible:
$list.servers.server[0].ip = "10.10.10.11"
$list.servers
name ip
—- —
PC1  10.10.10.11
PC2  10.10.10.12
# The result could be output again as text, including the
# modification:
$list.get_InnerXML()
<servers><server name="PC1" ip="10.10.10.11" />
<server name="PC2" ip="10.10.10.12" /></servers>

Variable type Description Example
[array] An array
[bool] Yes-no value [boolean]$flag = $true
[byte] Unsigned 8-bit integer, 0…255 [byte]$value = 12
[char] Individual unicode character [char]$a = "t"
[datetime] Date and time indications [datetime]$date = "12.Nov 2004 12:30"
[decimal] Decimal number [decimal]$a = 12
$a = 12d
[double] Double-precision floating point decimal $amount = 12.45
[guid] Globally unambiguous 32-byte identification number [guid]$id = [System.Guid]::NewGuid()
$id.toString()
[hashtable] Hash table
[int16] 16-bit integer with characters [int16]$value = 1000
[int32], [int] 32-bit integers with characters [int32]$value = 5000
[int64], [long] 64-bit integers with characters [int64]$value = 4GB
[nullable] Widens another data type to include the ability to contain null values. It can be used, among others, to implement optional parameters [Nullable“1[[System.DateTime]]]$test = Get-Date
$test = $null
[psobject] PowerShell object
[regex] Regular expression $text = "Hello World"
[regex]::split($text, "lo")
[sbyte] 8-bit integers with characters [sbyte]$value = -12
[scriptblock] PowerShell scriptblock
[single], [float] Single-precision floating point number [single]$amount = 44.67
[string] String [string]$text = "Hello"
[switch] PowerShell switch parameter
[timespan] Time interval [timespan]$t = New-TimeSpan $(Get-Date) "1.Sep 07"
[type] Type
[uint16] Unsigned 16-bit integer [uint16]$value = 1000
[uint32] Unsigned 32-bit integer [uint32]$value = 5000
[uint64] Unsigned 64-bit integer [uint64]$value = 4GB
[xml] XML document

Table 3.5: Commonly used .NET data types

Variable Management: Behind the Scenes

Whenever you create a new variable in PowerShell, it is stored in a PSVariable object. This object contains not just the value of the variable, but also other information, such as the description that you assigned to the variable or additional options like write-protection.

If you retrieve a variable in PowerShell, PowerShell will return only the variable value. If you'd like to see the remaining information that was assigned to the variable, you'll need the underlying PSVariable object. Get-Variable will get it for you:

$testvariable = "Hello"
$psvariable = Get-Variable testvariable

You can now display all the information about $testvariable by outputting $psvariable. Pipe the output to the cmdlet Select-Object to see all object properties and not just the default properties:

$psvariable | Select-Object
Name : testvariable
Description :
Value : Hello
Options : None
Attributes : {}
  • Description: The description you specified for the variable.
  • Value: The value assigned currently to the variable (i.e. its contents).
  • Options: Options that have been set, such as write-protection or AllScope.
  • Attributes: Additional features, such as permitted data type of a variable for strongly typed variables. The brackets behind Attributes indicate that this is an array, which can consist of several values that can be combined with each other.

Modification of Variables Options

One reason for dealing with the PSVariable object of a variable is to modify the variable's settings. Use either the cmdlet Set-Variable or directly modify the PSVariable object. For example, if you'd like to change the description of a variable, you can get the appropriate PSVariable object and modify its Description property:

# Create new variable:
$test = "New variable"

# Create PSVariable object:
$psvariable = Get-Variable test

# Modify description:
$psvariable.Description = "Subsequently added description"
Dir variable:test | Format-Table name, description

Name Description
—- ———–
test Subsequently added description
# Get PSVariable object and directly modify the description:
(Get-Variable test).Description =
"An additional modification of the description."
Dir variable:test | Format-Table name, description
Name Description
—- ———–
test An additional modification of the description.
# Modify a description of an existing variable with Set-Variable:
Set-Variable test -description "Another modification"
Dir variable:test | Format-Table name, description
Name Description
—- ———–
test Another modification

As you can see in the example above, you do not need to store the PSVariable object in its own variable to access its Description property. Instead, you can use a sub-expression, i.e. a statement in parentheses. PowerShell will then evaluate the contents of the sub-expression separately. The expression directly returns the required PSVariable object so you can then call the Description property directly from the result of the sub-expression. You could have done the same thing by using Set-Variable. Reading the settings works only with the PSVariable object:

(Get-Variable test).Description
An additional modification of the description.

Write-Protecting Variables

For example, you can add the ReadOnly option to a variable if you'd like to write-protect it:

$Example = 10

# Put option directly in PSVariable object:
(Get-Variable Example).Options = "ReadOnly"

# Modify option as wish with Set-Variable; because the variable
# is read-only, -force is required:
Set-Variable Example -option "None" -force

# Write-protection turned off again; variable contents may now
# be modified freely:
$Example = 20

The Constant option must be set when a variable is created because you may not convert an existing variable into a constant.

# A normal variable may not be converted into a constant:
$constant = 12345
(Get-Variable constant).Options = "Constant"
Exception in setting "Options": "The existing variable "constant"
may not be set as a constant. Variables may only be set as
constants when they are created."
At line:1 char:26
+ (Get-Variable constant).O <<<< options = "Constant"

Option Description
"None" NO option (default)
"ReadOnly" Variable contents may only be modified by means of the -force parameter
"Constant" Variable contents can't be modified at all. This option must already be specified when the variable is created. Once specified this option cannot be changed.
"Private" The variable is visible only in a particular context (local variable).
"AllScope" The variable is automatically copied in a new variable scope.

Table 3.6: Options of a PowerShell variable

Examining Strongly Typed Variables

Once you assign a specific data type to a variable as shown above, PowerShell will add this information to the variable attributes. .

If you delete the Attributes property, the variable will be unspecific again so in essence you remove the strong type again:

# List attributes and delete:
(Get-Variable a).Attributes
TypeId
——
System.Management.Automation.ArgumentTypeConverterAttribute
# Delete type specification:
(Get-Variable a).Attributes.Clear()

# Strong type specification is removed; now the variable can
# store text again:
$a = "Test"

Validating Variable Contents

The Attributes property of a PSVariable object can include additional conditions, such as the maximum length of a variable. In the following example, a valid length from two to eight characters is assigned to a variable. An error will be generated if you try to store text that is shorter than two characters or longer than eight characters:

$a = "Hello"
$aa = Get-Variable a
$aa.Attributes.Add($(New-Object `
System.Management.Automation.ValidateLengthAttribute `
-argumentList 2,8))
$a = "Permitted"
$a = "This is prohibited because its length is not from 2 to 8 characters"
Because of an invalid value verification (Prohibited because
its length is not from 2 to 8 characters) may not be carried out for
the variable "a".
At line:1 char:3
+ $a <<<< = "Prohibited because its length is not from 2 to 8

In the above example Add() method added a new .NET object to the attributes with New-Object. You'll learn more about New-Object in Chapter 6. Along with ValidateLengthAttribute, there are additional restrictions that you can place on variables.

Restriction Category
Variable may not be zero ValidateNotNullAttribute
Variable may not be zero or empty ValidateNotNullOrEmptyAttribute
Variable must match a Regular Expression ValidatePatternAttribute
Variable must match a particular number range ValidateRangeAttribute
Variable may have only a particular set value ValidateSetAttribute

Table 3.7: Available variable validation classes

In the following example, the variable must contain a valid e-mail address or all values not matching an e-mail address will generate an error. The e-mail address is defined by what is called a Regular Expression. You'll learn more about Regular Expressions in Chapter 13.

$email = "tobias.weltner@powershell.com"
$v = Get-Variable email
$pattern = "b[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}b"
$v.Attributes.Add($(New-Object `
System.Management.Automation.ValidatePatternAttribute `
-argumentList $pattern))
$email = "valid@email.de"
$email = "invalid@email"
Because of an invalid value verification (invalid@email) may not
be carried out for the variable "email".
At line:1 char:7
+ $email <<<< = "invalid@email"

If you want to assign a set number range to a variable, use ValidateRangeAttribute. The variable $age accepts only numbers from 5 to 100:

$age = 18
$v = Get-Variable age
$v.Attributes.Add($(New-Object `
System.Management.Automation.ValidateRangeAttribute `
-argumentList 5,100))
$age = 30
$age = 110
Because of an invalid value verification (110) may not be
carried out for the variable "age".
At line:1 char:7
+ $age <<<< = 110

If you would like to limit a variable to special key values, ValidateSetAttribute is the right option. The variable $option accepts only the contents yes, no, or perhaps:

$option = "yes"
$v = Get-Variable option
$v.Attributes.Add($(New-Object `
System.Management.Automation.ValidateSetAttribute `
-argumentList "yes", "no", "perhaps"))
$option = "no"
$option = "perhaps"
$option = "don't know"
Verification cannot be performed because of an invalid value
(don't know) for the variable "option".
At line:1 char:8
+ $option <<<< = "don't know"

Summary

Variables store information. Variables are by default not bound to a specific data type, and once you assign a value to a variable, PowerShell will automatically pick a suitable data type. By strongly-typing variables, you can restrict a variable to a specific data type of your choice. You strongly-type a variable by specifying the data type before the variable name:

# Strongly type variable a:
[Int]$a = 1

You can prefix the variable name with "$" to access a variable. The variable name can consist of numbers, characters, and special characters, such as the underline character "_". Variables are not case-sensitive. If you'd like to use characters in variable names with special meaning to PowerShell (like parenthesis), the variable name must be enclosed in brackets. PowerShell doesn't require that variables be specifically created or declared before use.

There are pre-defined variables that PowerShell will create automatically. They are called "automatic variables." These variables tell you information about the PowerShell configuration. For example, beginning with PowerShell 2.0, the variable $psversiontable will dump the current PowerShell version and versions of its dependencies:

PS > $PSVersionTable
Name                      Value
—-                      —–
CLRVersion                2.0.50727.4952
BuildVersion              6.1.7600.16385
PSVersion                 2.0
WSManStackVersion         2.0
PSCompatibleVersions      {1.0, 2.0}
SerializationVersion      1.1.0.1
PSRemotingProtocolVersion 2.1

You can change the way PowerShell behaves by changing automatic variables. For example, by default PowerShell stores only the last 64 commands you ran (which you can list with Get-History or re-run with Invoke-History). To make PowerShell remember more, just adjust the variable $MaximumHistoryCount:

PS > $MaximumHistoryCount
64
PS > $MaximumHistoryCount = 1000
PS > $MaximumHistoryCount
1000

PowerShell will store variables internally in a PSVariable object. It contains settings that write-protect a variable or attach a description to it (Table 3.6). It's easiest for you to set this special variable options by using the New-Variable or Set-Variable cmdlets (Table 3.1).

Every variable is created in a scope. When PowerShell starts, an initial variable scope is created, and every script and every function will create their own scope. By default, PowerShell accesses the variable in the current scope, but you can specify other scopes by adding a prefix to the variable name: local:, private:, script:, and global:.