Using $_

The $_ symbol seems to be causing confusion from some recent forum questions I’ve seen.

$_ represents the current object on the pipeline – if you want to know why $_ was chosen you’ll have to read PowerShell in Action!

You can use $_ in a number of situations – in commands that perform an action on every object (or selected objects) on the pipeline. Here’s some of the commoner usages:

In the early versions of PowerShell you used it in the filter script of Where-Object

Get-Process | Where-Object -FilterScript {$_.CPU -gt 50}

This is more usually written as

Get-Process | Where {$_.CPU -gt 50}

As of PowerShell v3 if you are filtering on ONE property you can simplify the syntax

Get-Process | Where CPU -gt 50

which is a truncated version of

Get-Process | Where -Property CPU -gt -Value 50

When you write it like this its obvious what is happening. Get in the habit of thinking of the syntax in this manner even if you write in the shortened form

If you need to filter on TWO or more properties you have to use the old style syntax

Get-Process | Where {$_.CPU -gt 50 -AND $_.Handles -gt 1000}

In all of these cases you’re comparing a property of the current pipeline object against a value. If the comparison is true the object is passed onto the next step of the pipeline. If its false the object is discarded.

You can use $psitem in place of $_ if you prefer

Get-Process | Where {$psitem.CPU -gt 50}

In the Foreach-Object cmdlet you can use $_ to refer to the current object

Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetEnabled = $true" |
ForEach-Object {
$ip = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index = $($_.DeviceId)"
$props = @{
Name = $_.NetConnectionId
Product = $_.ProductName
DHCP = $ip.DHCPEnabled
IP   = $ip.IPAddress
}
New-Object -TypeName PSObject -Property $props
}

$psitem also works in this situation

The third common usage of $_ is in select-object in calculated fields (you can do the same in Format-Table)

Get-CimInstance -ClassName Win32_LOgicalDisk -Filter "DriveType=3" |
select DeviceId, VolumeName,
@{N='Size(GB)'; E={[math]::Round($_.Size / 1GB, 2)}},
@{N='Used(GB)'; E={[math]::Round(($_.Size - $_.FreeSpace) / 1GB, 2)}},
@{N='Free(%)'; E={[math]::Round(($_.FreeSpace / $_.Size) * 100, 2 )}}

In this example we’re changing the size to GB from bytes and calculating the used space and the % free space. Again you could use $psitem instead of $_

ONE PLACE YOU CAN’T USE $_  IS IN A FOREACH LOOP

This fails

$nics = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetEnabled = $true"
$data = foreach ($nic in $nics) {
$ip = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index = $($_.DeviceId)"
$props = @{
Name = $_.NetConnectionId
Product = $_.ProductName
DHCP = $ip.DHCPEnabled
IP   = $ip.IPAddress
}
New-Object -TypeName PSObject -Property $props
}
$data |Format-List

To recap $_ (or $psitem) is used to represent the current object on the pipeline. You can us it in commands that are performing an action on every object on the pipeline.

Advertisements
This entry was posted in Powershell. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s