2.2 The Basics — Cmdlets, Variables, Advanced Functions, and Modules
PowerShell is created upon a few core
building blocks. A thorough understanding of these constituent parts
will make understanding the environment a lot easier, so the following
sections describe these core components.
Cmdlets
Cmdlets follow a naming convention to assist with identification. This convention is [Name]-[Verb], such as Get-Process, Get-Help, and Remove-Item.
Cmdlets are .NET classes that provide a single
piece of functionality and are either provided with the PowerShell
environment or provided by .NET developers and installed into the
environment. They can take PowerShell objects as input and provide
PowerShell objects as output. This ability to take and return
PowerShell objects enabled the framework creators to offer what’s known
as a pipeline. Using pipelines, you can construct single lines of
PowerShell that contain multiple cmdlets, each passing the output as
input to the next, with cmdlets participating in the pipeline separated
by the bar operator (|). Very powerful functionality can be composed in
a single line of PowerShell using this functionality. The following
example uses the Get-Process cmdlet to list all the running processes on the computer, and then the output of the Get-Process cmdlet is connected to the Export-Csv cmdlet, which exports the list of processes to a CSV file:
Get-Process | Export-Csv .\processes.csv -NoTypeInformation
Discovering functionality is important
in the command line because unlike in a GUI where you can visually scan
through the menu items, you need to instead use discoverability tools
that are provided with PowerShell. The first cmdlet to be aware of is Get-Help. Get-Help takes one parameter CmdletName or TopicName. You can also provide a few properties: -examples to display the examples; -detailed to get further information; and -full for all the technical information. The following example uses Get-Help to retrieve all the information about the Get-Process cmdlet:
Get-Help Get-Process -full
In order to “Get-Help” you need to know the cmdlets that are available, and PowerShell provides the Get-Command
cmdlet for just that purpose. If you are trying to find all the cmdlets
available within a module, you can specify the module name to filter
the results to only the commands that exist within the module. The
module name for SQL Server 2012 is SQLPS, so you can find all the
cmdlets provided with SQL Server 2012 with the following:
Get-Command -Module sqlps
If you don’t have the sqlps module
available the cmdlets will return nothing. You can make the module
available for use by importing it as shown below:
Import-Module sqlps
Aliases enable cmdlets to have different names, behavior embraced by the PowerShell team. As mentioned earlier, for example, DIR is available in PowerShell. It isn’t in fact the DIR command you’ll find in the cmd.exe but rather a PowerShell Cmdlet called Get-ChildItem that has been aliased to DIR for backward compatibility. Interestingly Get-ChildItem is also aliased to LS, which is the equivalent command on UNIX platforms.
Variables
After you have written cmdlet
statements in PowerShell, you need to be able to store data so that it
can be retrieved and manipulated further through your scripts.
PowerShell provides variables to store data within PowerShell.
Variables always start with a $ symbol, followed by the name you choose. For example, to store the top 10 processes by CPU usage you can use the following:
$TopTenProcessesByCPU = Get-Process | Sort-Object cpu -Descending | Select-Object -
First 10
Now that the value is stored in a variable, it is available for retrieval and further use through your script.
You can discover all the variables that are available using the Get-Variable cmdlet. Table 2 shows some of the more important variables that were returned after I ran this on my machine.
TABLE 2: Common PowerShell Variables
Variable Name |
Description |
$_ |
Current item in the pipeline |
$args |
Array of arguments |
$Error |
Array of errors |
$FALSE |
Boolean False |
$HOME |
Folder containing the current user’s profile |
$Host |
This is a reference to the host of this runspace. |
$null |
References to the null variable always return the null value. Assignments have no effect. |
$PID |
Current process ID |
$PROFILE |
Path to the active Profile |
$PSCulture |
Culture of the current Windows PowerShell session |
$PSEmailServer |
Variable to hold the Email Server. This can be used instead of the HostName parameter in the Send-MailMessage cmdlet. |
$PSHOME |
Parent folder of the host application of this Runspace |
$PSUICulture |
UI Culture of the current Windows PowerShell Session |
$PSVersionTable |
Version information for current PowerShell session |
$PWD |
PowerShell Working Directory |
$StackTrace |
Detailed StackTrace for the last error |
$TRUE |
Boolean True |
The most important variable to familiarize yourself with is $_,
which is the current object in the pipeline. This is very handy when
you want to iterate through all the items that are being sent through
the pipeline. The following example uses a Where-Object cmdlet to filter the output on every process that has been passed along the pipeline where the WorkingSet of the current process in the pipeline is greater than 100MB:
Get-Process | Where-Object {$_.WorkingSet –gt 100MB}
Note that this example uses the -gt comparison operator. PowerShell provides a set of comparison operators that need to be used when comparing objects. Table 3 lists the commonly used comparison operators.
TABLE 3: PowerShell Equality Operators
Operator |
Description |
-eq |
Equal to |
-ne |
Not equal to |
-gt |
Greater than |
-ge |
Greater than or equal to |
-lt |
Less than |
-le |
Less than or equal to |
-like |
Matches using the (*) wildcard character |
Operator |
Description |
-notlike |
Does not match using the (*) wildcard character |
-match |
Matches a string using a regular expression |
-notmatch |
Does not match a string using a regular expression |
-contains |
Includes an identical value |
-notcontains |
Does not include an identical value |
-band |
Bitwise AND |
-bor |
Bitwise OR |
-bxor |
Bitwise XOR |
It is often useful to know the makeup of a variable. You can discover the structure of an object using the Get-Member cmdlet. The following code demonstrates the creation of a new string object and then passing it into the Get-Member cmdlet:
PS > [String]$NewString | Get-Member
Knowing the members available on an
object is very useful because you can start using them. For instance,
in the following example I have a Statement string and want to return
the last word. By inspecting the members on the $Statement variable, I can see that there is a Length property, an IndexOf method, and a Substring method, which I can use together to return the part of the string I’m interested in (code file: PS_StringManipulation01.PS1):
$Statement = "PowerShell Rocks"
$Statement.SubString($Statement.IndexOf(" ") + 1, $Statement.Length -
$Statement.IndexOf(" ") - 1)
Advanced Functions
Once you have used a snippet of
PowerShell code a few times you’ll probably want a way of refactoring
that code into something that you can reuse easily. Fortunately,
PowerShell 2.0 makes this very easy to accomplish with a new feature
called advanced functions. I’m going to use a trivial example to demonstrate this functionality. Assume that you regularly filter the Get-Process cmdlet for all processes starting with SQL, and you want to be able to return all these processes with a simple cmdlet named Get-SQLProcess. The following code listing shows how this advanced function is created:
function Get-SQLProcess
{
<#
.SYNOPSIS
Retrieves processes starting with the term SQL
.DESCRIPTION
The Get-SQLProcess function uses the Get-Process Cmdlet to retrieve all the
processes
that start with SQL from the local computer.
#>
[CmdletBinding()]
Param()
Process
{
Get-Process SQL*
}
}
After this code has been executed by the PowerShell environment, you’ll be able to call this function; and because it uses the CmdletBinding
attribute, which differentiates the advanced function from a standard
function, PowerShell will treat it like a compiled cmdlet — meaning it
will have autocomplete and be listed among the available cmdlets. I’ve
also written some simple documentation for the preceding example, so
this function now has help as well. The following listing shows the
documentation that is displayed for this function when Get-Help is called on it:
PS > Get-Help Get-SQLProcess
NAME
Get-SQLProcess
SYNOPSIS
Retrieves processes starting with the term SQL
SYNTAX
Get-SQLProcess [<CommonParameters>]
DESCRIPTION
The Get-SQLProcess function uses the Get-Process Cmdlet to retrieve all the
processes that start with SQL from the local computer.
RELATED LINKS
REMARKS
To see the examples, type: "get-help Get-SQLProcess -examples".
For more information, type: "get-help Get-SQLProcess -detailed".
For technical information, type: "get-help Get-SQLProcess -full".
Modules
Modules are another new feature
introduced in PowerShell 2.0. Before modules were available, developers
who wanted to introduce new functionality into PowerShell were required
to use snap-ins, which were created in the C# programming language,
compiled, and then imported into the PowerShell host. This was
difficult and required the assistance of an experienced C# developer to
introduce new functionality. Modules are designed to make this easier,
as a module is a package that can contain members such as cmdlets,
providers, functions, variables, aliases, and so on. There are four
types of module, described in the following sections.
Script Modules
A PowerShell script that has a .PSM1 file extension is known as a script module.
Modules contain PowerShell script that can be shared within an
organization and, if required, further afield. Adding advanced
functions as shown in the last section enables the script author to
create cmdlet-like functionality and share it.
Script modules are the most accessible way to
create a module because any valid PowerShell script can simply be saved
into a file with a .PSM1 extension and then be used as any other module type.
Binary Modules
A binary module contains compiled .NET code and is compiled into an assembly (.dll).
This is essentially a replacement for the snap-in functionality
provided in PowerShell 1.0. The disadvantage of using binary modules is
that the assistance of an experienced C# developer was required to
create the modules. However, if significant intellectual property is
contained within the module, this may be the best approach because the
code can be obfuscated before it is distributed.
Manifest Modules
Manifest modules are used to describe
the contents of a module. They can contain the prerequisites
(PowerShell version, .NET Framework version, etc.); processing
directives such as scripts; formats; and type properties. Restrictions
can be applied, such as members of the module to export. This is useful
when creating a clean API for others to share. By convention, manifest
files have a .psd1 extension, and formatting and type files have a .psxml extension.
Dynamic Modules
Dynamic modules are created on demand using the New-Module
cmdlet. These modules live in memory only for the session in which the
PowerShell host is active, and because of this transient nature they
are not exposed through the Get-Module cmdlet.
Working with Modules
To identify the modules available on your system, you can use the Get-Module cmdlet with the -ListAvailable
parameter, which returns a list of all the modules that can be imported
into the session. The following example shows the SQL modules available
on my system:
PS > get-module -listavailable sql*
ModuleType Name ExportedCommands
---------- ---- ----------------
Manifest SQLASCMDLETS {}
Manifest SQLPS {}
Once you have identified a module that you would like to use, you can import it using the Import-Module
cmdlet. You need to provide the name of the module if it is listed in
the available modules or by entering the full path of the module. When
importing a module, there is a parameter called -DisableNameChecking that prevents PowerShell from checking the members’ verbs against a predefined, approved list from the PowerShell product team.
SQL Server 2012 comes with two modules, SQLPS and
SQLASCMDLETS; the following example script imports both modules into
the active session:
Import-Module SQLPS,SQLASCMDLETS -DisableNameChecking
I like to inspect the cmdlets that are
available when importing a module for the first time so that I can find
out what I can do with the module. This is possible using the Get-Command cmdlet with the -module
parameter to filter the cmdlets listed to only those for the given
module. The following example shows the cmdlets available within the
SQLASCMDLETS module:
PS SQLSERVER:\> Get-Command -Module SQLASCMDLETS
CommandType Name
----------- ----
Cmdlet Add-RoleMember
Cmdlet Backup-ASDatabase
Cmdlet Invoke-ASCmd
Cmdlet Invoke-ProcessCube
Cmdlet Invoke-ProcessDimension
Cmdlet Invoke-ProcessPartition
Cmdlet Merge-Partition
Cmdlet New-RestoreFolder
Cmdlet New-RestoreLocation
Cmdlet Remove-RoleMember
Cmdlet Restore-ASDatabase
If you no longer want to reference a module imported into a session, you can remove it using the Remove-Module cmdlet, providing the name of the module you wish to remove.
Signing PowerShell Scripts
Modules are designed to be
shared and should therefore be digitally signed so that users who have
locked down their computer with Set-ExecutionPolicy
will still be able to execute the script — unless the computer is
locked down to Restricted, in which case no scripts can be executed.