PowerShell has some cmdlets that are designed to work with all kinds of objects. You can easily recognize them because they all have the noun Object
. You can use the Get-Command
cmdlet -Noun
parameter to find them:
PowerCLI C:> Get-Command -Noun Object
The preceding command has the following output:
CommandType Name Version Source ----------- ---- ------- ------ Cmdlet Compare-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet ForEach-Object 3.0.0.0 Microsoft.PowerShell.Core Cmdlet Group-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet Measure-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet New-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet Select-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet Sort-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet Tee-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet Where-Object 3.0.0.0 Microsoft.PowerShell.Core
In the following section, we will discuss the Object
cmdlets.
If you want to retrieve a subset of the properties of an object, select unique objects or a specific number of objects or specific objects from an array, you can use the Select-Object
cmdlet. You can also use the Select-Object
cmdlet to add properties to an object using calculated properties, as you have seen in
Chapter 2
, Learning Basic PowerCLI Concepts.
The Select-Object
cmdlet has the following syntax. The first parameter set is the default:
Select-Object [[-Property] <Object[]>] [-ExcludeProperty <String[]>] [-ExpandProperty <String>] [-First <Int32>] [-InputObject <PSObject>] [-Last <Int32>] [-Skip <Int32>] [-Unique] [-Wait] [<CommonParameters>]
The second parameter set can be used to skip the last part of the output:
Select-Object [[-Property] <Object[]>] [-ExcludeProperty <String[]>] [-ExpandProperty <String>] [-InputObject <PSObject>] [-SkipLast <Int32>] [-Unique] [<CommonParameters>]
The third parameter set can be used to specify an array of objects to return based on their index values:
Select-Object [-Index <Int32[]>] [-InputObject <PSObject>] [-Unique] [-Wait] [<CommonParameters>]
You can use Select
as an alias for the Select-Object
cmdlet.
The Get-VM
cmdlet returns the Name
, PowerState
, NumCpu
, and MemoryGB
properties of the virtual machines by default. But what if you want to return the Name
, VMHost
, and Cluster
properties instead? If you look at the properties of a virtual machine object, you will see the VMhost
property. But you will not see a Cluster
property. However, if you look at a VMHostImpl
object, you will find a Parent
property. The Parent
property of a VMhostImpl
object contains the cluster that the host is a member of.
The preceding command uses the Select-Object
cmdlet to select the Name
and VMHost
properties of the virtual machine objects, and it creates a calculated property named Cluster
that retrieves the cluster via the VMHost.Parent
property.
You can use the Select-Object -First
parameter to specify the number of objects to select from the beginning of an array of input objects. For example, to retrieve the first three host types, use the following command:
PowerCLI C:> Get-VMHost | Select-Object -First 3
If you are typing commands at the pipeline, you can also use their aliases. For Select-Object
, the alias is Select
. So, the next example will give the same result as the preceding one:
PowerCLI C:> Get-VMHost | Select -First 3
To select a number of objects starting from the end of an array of objects, use the Select-Object -Last
parameter. The following command retrieves the last cluster:
PowerCLI C:> Get-Cluster | Select-Object -Last 1
You can also skip objects from the beginning or the end of an array using the Select-Object -Skip
parameter. The following command returns all of the folder objects except the first two:
PowerCLI C:> Get-Folder | Select-Object -Skip 2
A very interesting parameter of the Select-Object
cmdlet is the -ExpandProperty
parameter. You can use this parameter to expand the object if the property contains an object. For example, if you want to get the VMHostImpl
object of the virtual machine named dc1
, you can execute the following command:
PowerCLI C:> Get-VM -Name dc1 | Select-Object
-ExpandProperty VMHost
If you only want a subset of all of the objects that a command returns, you can use the Where-Object
cmdlet to filter the output of a command and only return the objects that match the criteria of the filter.
The Where-Object
cmdlet syntax definition is so long that it'll take too much space in this book. You can easily get the Where-Object
cmdlet syntax with the following command:
PowerCLI C:> Get-Help Where-Object
PowerShell V3 introduced a new, easier syntax for the Where-Object
cmdlet. I will show you both the V2 and V3 syntaxes. First, let's see the new PowerShell V3 syntax.
Let's try to find all virtual machines that have only one virtual CPU. You can do this by searching for virtual machines that have a NumCPU
property with a value of 1
:
PowerCLI C:> Get-VM | Where-Object NumCpu -eq 1 Name PowerState Num CPUs MemoryGB ---- ---------- -------- -------- DC1 PoweredOff 1 0.250
If you use the alias Where
, the command looks more like a natural language:
PowerCLI C:> Get-VM | Where NumCpu -eq 1
You can also use the alias ?
if you want to type less on the command line:
PowerCLI C:> Get-VM | ? NumCpu -eq 1
The PowerShell V2 syntax is a bit more obscure. You have to use a script block as the value of the Where-Object -FilterScript
parameter:
PowerCLI C:> Get-VM | Where-Object -FilterScript {$_.NumCpu -eq 1}
Because the -FilterScript
parameter is the first positional parameter of the Where-Object
cmdlet, nobody uses the parameter name, and you will always see one of the following command lines being used:
PowerCLI C:> Get-VM | Where-Object {$_.NumCpu -eq 1} PowerCLI C:> Get-VM | where {$_.NumCpu -eq 1}
The advantage of the PowerShell V2 syntax over the V3 syntax is that you can create complex filtering scripts. For example:
PowerCLI C:> Get-VM | >> Where-Object {$_.NumCpu -gt 2 -and $_.MemoryGB -lt 16} >>
The preceding command will show you all of the virtual machines with more than two virtual CPUs and less than 16 GB of memory. If you want to create the same filter using the PowerShell V3 syntax, you have to use two filters: one for the number of CPUs and one for the memory:
PowerCLI C:> Get-VM | Where NumCpu -gt 2 | Where MemoryGB -lt 16
Some cmdlets don't accept properties from the pipeline. On the other hand, you would like to use a cmdlet in the pipeline, but the property you want to use in the pipeline doesn't accept pipeline input. This is where the PowerShell ForEach-Object
cmdlet will help you.
The ForEach-Object
cmdlet has the following syntax. The first parameter set is for manipulating properties of the input objects:
ForEach-Object [-MemberName] <String> [-ArgumentList <Object[]>] [-Confirm] [-InputObject <PSObject>] [-WhatIf] [<CommonParameters>]
The -MemberName
parameter is required.
The second parameter set is for processing script blocks for each object in the input:
ForEach-Object [-Process] <ScriptBlock[]> [-Begin <ScriptBlock>] [-Confirm] [-End <ScriptBlock>] [-InputObject <PSObject>] [-RemainingScripts <ScriptBlock[]>] [-WhatIf] [<CommonParameters>]
The -Process
parameter is required.
The default first parameter is -Process
, and that parameter name is most of the time omitted when using the cmdlet. The -Process
parameter has a scriptblock as its parameter value, and this scriptblock will run for every object that passes the pipeline. For example:
PowerCLI C:> Get-VM | ForEach-Object {$_.Name}
The preceding command will retrieve the names of all of your virtual machines. In the scriptblock, the special variable $_
is used. The $_
variable is the current object that passes through the pipeline. So, $_.Name
will return the value of the name property of the current object.
You can also use the alias foreach
:
PowerCLI C:> Get-VM | foreach {$_.Name}
In PowerShell V3, the command is even simpler:
PowerCLI C:> Get-VM | foreach Name
If you want the ForEach-Object
cmdlet to execute code before the objects start to pass through the pipeline, for example, to initialize a variable, you can create a scriptblock and use it as an argument value for the -Begin
parameter. If you want the ForEach-Object
cmdlet to execute code after the objects finish passing through the pipeline, for example, to return a result of a calculation you did on all of the objects in the pipeline, you can create a scriptblock and use it as an argument value for the -End
parameter. For example:
PowerCLI C:> 1,2,3,4 | >> ForEach-Object -Begin {"Start of the Script"; $Sum = 0} ` >> -Process {$Sum += $_} -End {"Sum of the elements: $Sum"} >> Start of the Script Sum of the elements: 10
The preceding example was just to demonstrate the ForEach-Object
cmdlet. If you want to take the sum of the objects in the pipeline, it is better to use the Measure-Object
cmdlet that will be discussed in one of the following sections.
In the example, you can see that the semicolon character is used to separate PowerShell commands. The backtick character (`
) used as the last character of a line is used to escape the newline character and treat the newline character as a space. This enables you to break long lines of code and continue them on the next line.
To specify 1
, 2
, 3
, 4
, you can also use the PowerShell range operator (..
):
1..4
The preceding operator is equivalent to the following:
1, 2, 3, 4
The ForEach-Object -Process
scriptblock is run at least once even if the input object is $null
, as you can see in the following example:
PowerCLI C:> $null | ForEach-Object -Process {"Hello world."} Hello world.
You probably don't want to return anything if the input object is $null
. You can solve this problem by testing inside the scriptblock if the input object exists with if ($_)
:
PowerCLI C:> $null | ForEach-Object -Process {if ($_) {"Hello."}}
PowerCLI returns objects in a random order. If you want the objects to be sorted, you can use the Sort-Object
cmdlet to sort them.
The Sort-Object
cmdlet has the following syntax:
Sort-Object [[-Property] <Object[]>] [-CaseSensitive] [-Culture <String>] [-Descending] [-InputObject <PSObject>] [-Unique] [<CommonParameters>]
The Sort-Object
cmdlet sorts objects in ascending or descending order based on the values of the properties of the object.
You can specify a single property or multiple properties (for a multi-key sort), and you can select a case-sensitive or case-insensitive sort. You can also direct Sort-Object
to display only the objects with a unique value for a particular property.
The following example will give a list of all of your virtual machines, and their hosts sorted in ascending order on the hostname and the virtual machine name:
PowerCLI C:> Get-VM | Select-Object -Property VMHost,Name | >> Sort-Object -Property VMHost,Name >>
Using the aliases select
for Select-Object
and sort
for Sort-Object
and also using positional parameters, the preceding example can be shortened into:
PowerCLI C:> Get-VM | select VMHost,Name | sort VMHost,Name
If you want to count objects or calculate the minimum, maximum, sum, and average of the numeric values of properties, you can use the Measure-Object
cmdlet. For text objects, it can count and calculate the number of lines, words, and characters.
The Measure-Object
cmdlet has the following syntax. The first parameter set is for counting the input objects and displaying the minimum, and maximum values, the sum, and average values of the input objects:
Measure-Object [[-Property] <String[]>] [-Average] [-InputObject <PSObject>] [-Maximum] [-Minimum] [-Sum] [<CommonParameters>]
The second parameter set is for counting characters, lines, and words in the input objects:
Measure-Object [[-Property] <String[]>] [-Character] [-IgnoreWhiteSpace] [-InputObject <PSObject>] [-Line] [-Word] [<CommonParameters>]
There are no required parameters.
Let's get back to our example from the ForEach-Object
cmdlet where we counted the sum of the numbers 1
, 2
, 3
, and 4
. You can do this in an easier way using the Measure-Object
cmdlet with the following command:
PowerCLI C:> (1..4 | Measure-Object -Sum).Sum 10
The only output property that the Measure-Object
cmdlet fills by default is the Count
property. To count the number of virtual machines in your environment, type the following command:
PowerCLI C:> Get-VM | Measure-Object Count : 12 Average : Sum : Maximum : Minimum : Property :
If you just want the count value, you can specify the Count
property:
PowerCLI C:> (Get-VM | Measure-Object).Count 12
Otherwise, you can use the Select-Object -ExpandProperty
parameter as follows:
PowerCLI C:> Get-VM | Measure-Object | >> Select-Object -ExpandProperty Count >> 12
You can also use the alias measure
for Measure-Object
:
PowerCLI C:> (Get-VM | measure).Count 12
The following command will retrieve the average, sum, maximum, and minimum values of the ProvisionedSpaceGB
property of all of your virtual machines:
PowerCLI C:> Get-VM | Measure-Object -Property ProvisionedSpaceGB -Average -Sum -Maximum -Minimum Count : 12 Average : 60.590376389601 Sum : 727.084516675211 Maximum : 221.090891393833 Minimum : 2.58988594077528 Property : ProvisionedSpaceGB
To count the number of characters, words, and lines in a string, you can use the -Line
, -Word
, and -Character
parameters:
PowerCLI C:> "Learning PowerCLI" | >> Measure-Object -Line -Word -Character >> Lines Words Characters Property ----- ----- ---------- -------- 1 2 17
You can also use the Measure-Object
cmdlet to count the number of lines, words, and characters in the here-string:
PowerCLI C:> @" >> Every vSphere admin should >> Learn PowerCLI! >> "@ | Measure-Object -Line -Word -Character >> Lines Words Characters Property ----- ----- ---------- -------- 2 6 42
If you don't want a value to have several fractional digits, you can use the .NET Math.Round
method to round a value to the nearest integer or the specified number of fractional digits:
Math.Round
method is the value that you want to roundIf you don't specify the second parameter, the value will be rounded to the nearest integer. For example:
PowerCLI C:> [math]::Round(60.590376389601,2) 60.59
You can use the Math.Round
method in a calculated property. For example:
PowerCLI C:> Get-VM | Measure-Object -Property ProvisionedSpaceGB -Average -Sum -Maximum -Minimum | >> Select-Object -Property Count, >> @{Name="Average";Expression={[math]::Round($_.Average,2)}} >> Count Average ----- ------- 12 60.59
The Group-Object
cmdlet group's objects contain the same value for the specified properties. Using the Group-Object
cmdlet can be very useful, for example, when you want to count the number of instances of a specific value of a property.
The Group-Object
cmdlet has the following syntax:
Group-Object [[-Property] <Object[]>] [-AsHashTable] [-AsString] [-CaseSensitive] [-Culture <String>] [-InputObject <PSObject>] [-NoElement] [<CommonParameters>]
Let's create a PowerCLI command to count the number of instances of each operating system installed on a virtual machine. In a traditional scripting language, you would have to loop through all of your virtual machines and increment counters for each operating system. In PowerCLI, you can achieve this much easier using the Group-Object
cmdlet. The name of the guest operating system is in a virtual machine's Guest.OSFullName
property. You can use the Group-Object
cmdlet to group the objects on the Guest.OSFullName
property using a calculated property. The Group-Object
cmdlet will return the Count
, Name
, and Group
properties by default. Using the -NoElement
parameter will remove the Group
property from the output. The following example groups the virtual machines on the Guest.OSFullname
property and returns the Count
and Name
properties for each Guest.OSFullName
:
PowerCLI C:> Get-VM | >> Group-Object -Property @{Expression= {$_.Guest.OSFullName}} -NoElement | >> Format-Table -AutoSize >> Count Name ----- ---- 1 Microsoft Windows Server 2012 (64-bit) 1 SUSE Linux Enterprise 11 (32-bit) 3 SUSE Linux Enterprise 11 (64-bit) 1 2 Microsoft Windows Server 2008 R2 (64-bit) 1 CentOS 4/5/6 (32-bit) 2 Other (64-bit) 1 CentOS 4/5/6 (64-bit) 1 CentOS 4/5/6 (64-bit)
The Format-Table -AutoSize
command formats the output, so that you will get the full operating system name.
If you want to compare two objects or sets of objects, you have to use the Compare-Object
cmdlet.
The Compare-Object
cmdlet has the following syntax:
Compare-Object [-ReferenceObject] <PSObject[]> [-DifferenceObject] <PSObject[]> [-CaseSensitive] [-Culture <String>] [-ExcludeDifferent] [-IncludeEqual] [-PassThru] [-Property <Object[]>] [-SyncWindow <Int32>] [<CommonParameters>]
From the sets of objects that the Compare-Object
cmdlet compares one set of objects is named the reference set, and the other set is named the difference set.
Let's compare two strings:
PowerCLI C:> $string1 = "Learning PowerCLI" PowerCLI C:> $string2 = "Learning PowerCLI!" PowerCLI C:> Compare-Object -ReferenceObject $string1 ` >> -DifferenceObject $string2 >> InputObject SideIndicator ----------- ------------- Learning PowerCLI! => Learning PowerCLI <=
The output shows that the strings are not the same. The <=
symbol indicates objects from the reference set. The =>
symbol indicates objects from the difference set. If you use the -IncludeEqual
parameter, the Compare-Object
cmdlet will also show objects that are equal in both sets. These objects are indicated by the ==
symbol.
The Tee-Object
cmdlet saves the command output in a file or a variable and also sends it down the pipeline. If Tee-Object
is the last command in the pipeline, the command output is displayed at the prompt.
The Tee-Object
cmdlet has the following syntax. The first parameter set is for saving the output in a file:
Tee-Object [-FilePath] <String> [-Append] [-InputObject <PSObject>]
[<CommonParameters>]
The -FilePath
parameter is required. The second parameter set is for saving the output in a file using a literal path:
Tee-Object [-InputObject <PSObject>] -LiteralPath <String>
[<CommonParameters>]
The -LiteralPath
parameter is required. The third parameter set is for saving the output in a variable:
Tee-Object [-InputObject <PSObject>] -Variable <String>
[<CommonParameters>]
The -Variable
parameter is required.
The following example retrieves all of your virtual machines and saves the virtual machine objects in a variable named VMs
. It also sends the objects down the pipeline and displays the Name
and PowerState
properties of the virtual machines:
PowerCLI C:> Get-VM | Tee-Object -Variable VMs | >> Select-Object -Property Name,PowerState >>
The following figure shows that the Tee-Object
cmdlet writes its output to the variable $VMs
and pipes its output to the Select-Object
cmdlet in the pipeline:
The Tee-Object
cmdlet is named after the T-splitter used in plumbing. Doesn't the Tee-Object
cmdlet in the figure look like a T-splitter?
18.223.125.219