In-Depth

Find the Right Shortcut for You in PowerShell

Of the many ways to get things done in Windows PowerShell, it's best to figure out which one works and stick with that.

One of the things I both love and hate about Windows PowerShell is its inherent flexibility. In many ways, it's written like a programming language. If you're not a programmer, you can pretty much ignore that and treat it like a command-line shell.

Well, maybe not completely "ignore." You see, there always seem to be three or four different ways to do almost anything in Windows PowerShell. You only really need to know the one way that makes the most sense to you. A smart Windows PowerShell user, however, will be familiar with all the means with which to accomplish a given task. That way, when you're looking at examples written by other people, you'll be able to make sense of what you're seeing.

All About the Alias
Aliases are the first "shortcut" most of you will encounter. Consider this command:

PS C:\> Get-WmiObject -class Win32_LogicalDisk -Filter "DriveType=3" |
>> Where-Object -FilterScript{ $_.FreeSpace / $_.Size -lt .1 } |
>> Select-Object -Property DeviceID,FreeSpace,Size
>> 
You can shortcut it like so:
PS C:\>gwmi Win32_LogicalDisk -Fi "DriveType=3" |
>> ?{ $_.FreeSpace / $_.Size -lt .1 } |
>> SelectDeviceID,FreeSpace,Size
>> 

I've used aliases in place of command names, and I've truncated parameters to fewer characters. Windows PowerShell lets you truncate parameter names as long as you provide enough of each parameter name for it to be uniquely identified. This makes for less typing, but a much more difficult-to-read command. I counsel my students to spell everything out when they're publishing an example, saving it into a script or otherwise making it a permanent artifact. This will make it easier to identify later.

In the Windows PowerShell ISE and most other editors, you can type the truncated portion of the parameter name and hit Tab to finish typing the entire name. There's less typing -- and it's easier to read. I've completely omitted other parameter names, because they're positional parameters. In other words, as long as I remember to get all the values lined up in the right order, the command still works.

Again, truncated names aren't as easy to read. Most people will appreciate you using the full parameter name in a script. Using the full parameter names is ultimately less work on you as well, because you don't have to remember what order they come in. When you use names, you can put them in any order you want.

Expressions in Quotes
You will occasionally need to insert an expression result into a quoted string. This lets you include that result in your script's output. One way to do this is store the result in a variable. Then use the Windows PowerShell trick of inserting variables into double-quoted strings. Windows PowerShell will replace the variable with its contents:

$disk = Get-WmiObject -class Win32_LogicalDisk -filter "DeviceID='C:'"
$freepercent = $disk.freespace / $disk.size * 100
Write-Host "Free space on drive C: at $freepercent percent"
Windows PowerShell also lets you put the complete expression within double quotes. The syntax is derived from a Unix shell trick. It looks a little weird, but you'll see folks use it a lot:
$disk = Get-WmiObject -class Win32_LogicalDisk -filter "DeviceID='C:'"
Write-Host "Free space on drive C: at $($disk.freespace / $disk.size * 100) percent"

I prefer the first form, mainly because I tend to work mostly with Windows PowerShell beginners. The first form is a bit easier to read. One thing happens on each step of the script, making the logic a little easier to follow.

Windows PowerShell uses enough punctuation marks that the second form seems more confusing. However, the second form is also technically a bit more efficient. It isn't creating a variable that Windows PowerShell has to track. It also involves a wee bit less typing, which most of you seem to like.

Creating Objects
The long form again comes in handy when I need to create a custom object:

$cs = Get-WmiObject -class Win32_ComputerSystem
$os = Get-WmiObject -class Win32_OperatingSystem
$obj = New-Object -TypeNamePSObject
$obj | Add-Member -MemberTypeNoteProperty -Name OSVersion -Value $os.version
$obj | Add-Member -MemberTypeNoteProperty -Name Mfgr -Value $cs.manufacturer
$obj | Add-Member -MemberTypeNoteProperty -Name Model -Value $cs.model
Write-Output $obj

Sometimes I'll shortcut this just a bit using this technique, which combines the Add-Member commands into a single pipeline:

$cs = Get-WmiObject -class Win32_ComputerSystem
$os = Get-WmiObject -class Win32_OperatingSystem
$obj = New-Object -TypeNamePSObject
$obj | Add-Member -MemberTypeNoteProperty -Name OSVersion -Value $os.version –PassThru |
Add-Member -MemberTypeNoteProperty -Name Mfgr -Value $cs.manufacturer –PassThru |
Add-Member -MemberTypeNoteProperty -Name Model -Value $cs.model –PassThru
Recently, I've started switching to a slightly different technique, which is even more readable:
$cs = Get-WmiObject -class Win32_ComputerSystem
$os = Get-WmiObject -class Win32_OperatingSystem
$props = @{'OSVersion'= $os.version;
'Mfgr'=$cs.manufacturer;
'Model'=$cs.model}
$obj = New-Object -TypeNamePSObject -Property $props
Write-Output $obj

Again, my preference for this kind of long-form syntax is because of its readability. I work mainly with Windows PowerShell newcomers, so readability is crucial in helping them understand what's happening. You'll also see folks use this trick:

$obj = Select-Object OSVersion,Mfgr,Model
$obj.OSVersion = $os.version
$obj.Mfgr = $cs.manufacturer
$obj.Model = $cs.model
$obj

The results are basically the same, and that last one uses a bit less typing. It's kind of a hacker trick to use Select-Object that way. It's taking advantage of the fact that you can tell Select-Object to select a nonexistent property. It will actually create the property, with an empty value on the output object.

It actually pains me to see Select-Object used (or, more accurately, misused) that way, and I'm not the only one. As far back as 2008, some of my fellow Windows PowerShell MVPs were speaking out against this usage. Regardless of how you feel about this technique, you should learn to recognize it so that you'll know what it does.

In Windows PowerShell version 3, you'll actually be able to do this:

[pscustomobject]@{'OSVersion'= $os.version;
'Mfgr'=$cs.manufacturer;
'Model'=$cs.model}

I haven't decided how I feel about this syntax, but generally it's OK. It's still pretty readable and eliminates a bit of typing. People will begin using it as well, so you should recognize it for what it is. The last technique also seems to execute faster (read more about it on Jonathan Medd's blog).

Object Shortcuts
The last shortcut technique you'll often see is similar to expressions-in-strings. This is totally legal programming syntax -- it can just be confusing for newcomers. Still, there's nothing "wrong" with it, so you should get used to seeing it and know what it does.

Say you want to execute some method of an object, and then do something with one of the resulting object's properties, like so:

$string = '  Hello   '
$string = $string.Trim()
$string.Length

You can shortcut all that by using parentheses. The parenthetical expression is evaluated first, and Windows PowerShell basically replaces the parenthetical with its result:

$string = '   Hello   '
($string.Trim()).length

It stacks up the punctuation a lot, so it's a bit more difficult for your brain to pull the pieces apart and determine what's going on, but it's completely legal. It might even execute a tiny bit faster, because Windows PowerShell doesn't have to manipulate the variable as many times.

Sorry About All That
In some ways, it's unfortunate Windows PowerShell couldn't offer just one way to do anything. Then we'd all only have to learn that one way. As it is, we have a shell that offers lots of different syntax variations for what's essentially the same task. It pays to at least be familiar with the many syntax alternatives. That way, you can make use of whatever examples you encounter.

comments powered by Disqus
Upcoming Events

Redmond Tech Watch

Sign up for our newsletter.

I agree to this site's Privacy Policy.