PowerShell Basics

What Formatting cmdlets do to your data

I have seen an increasing number of questions recently where the answer has been to remove Format-Table from the pipeline. As an example consider the names of the processes running on your machine

Get-Process -Name calc | Stop-Process


works because you are piping the selected object into the Stop-Process cmdlet.

Now think about this

Get-Process -Name calc | select Name | Stop-Process


a bit more long winded (the select isn’t necessary) but the resultant object hitting Stop-Process identifies a process by name which is all Stop-Process needs to work.


As a way to approach the reason for the first sentence in the post is there any difference between these two statements?

Get-Process -Name calc

Get-Process -Name calc | Format-Table


The output to screen looks identical. So  I should be able to do this:

Get-Process -Name calc | Format-Table | Stop-Process


What I get is a bunch of errors of the form:

Stop-Process : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:1 char:41
+ Get-Process -Name calc | Format-Table | Stop-Process
+                                         ~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (Microsoft.Power...FormatStartData:PSObject) [Stop-Process], ParameterB
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.StopProcessCommand


The reason for this is simple.  The Format cmdlets destroy your pipeline and objects.  They take what they are given and output formatting directives. The only thing you can do with them is display them on screen (you can pipe to a text file but effectively the same thing)


If in doubt about what you’re dealing with at any time use Get-member

£> Get-Process | Get-Member

   TypeName: System.Diagnostics.Process


£> Get-Process | select name | Get-Member

   TypeName: Selected.System.Diagnostics.Process


Notice the slight change in that its now Selected.System.Diagnostics.Process instead of System.Diagnostics.Process

This only applies if you’re selecting a subset of properties. If you want the first N objects


£> Get-Process | select -First 5 | Get-Member

   TypeName: System.Diagnostics.Process


You still have the original type.


However, lets look at formatting

£> Get-Process | Format-Table | Get-Member

TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupStartData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupEndData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEndData


You get 5 different objects out – none of which have anything to do with your processes – apart from the values.

The Format cmdlets are designed to format your data for display. Thats all they do. Once your data hits the Format cmdlets that data can only be used fro display – nothing else. Put your Format cmdlets at the end of your pipeline and don’t attempt to do anything else with the data.

The little changes that make a difference

Each version of PowerShell introduces a new headline feature – remoting, workflows, DSC, OneGet in version 2,3,4 and 5 respectively. While this can change the way we work there are also a host of little changes that come along that are often overlooked.


One example is a change to Get-ChildItem introduced in PowerShell 3.0.


Consider getting a directory listing:

Get-ChildItem -Path C:\Windows


This will give all subfolders and file in the given folder.


If you just wanted the files you had to do this:

Get-ChildItem -Path C:\Windows | where {$_.PSIsContainer}


If you want just the files you use:
Get-ChildItem -Path C:\Windows | where {-not $_.PSIsContainer}


or the slightly shorter but not as easy to read:
Get-ChildItem -Path C:\Windows | where {!$_.PSIsContainer}


The PSIsContainer property name is not intuitive and I rarely remember the name exactly and try ISPSContainer first or some other variant.


Two additional filtering parameters were added to Get-ChildItem

Get-ChildItem -Path C:\Windows –Directory


Get-ChildItem -Path C:\Windows -File


produce listings of folders and files respectively.


A small simple change that makes life easier.


There are a lot of small changes like this scattered through the later PowerShell versions – I’d recommend going through the release notes to track down the ones that will be useful to you.

Output from jobs

I tripped over a little problem the other day that’s worth reporting.  I was running something like this:


$sb = {
$procs = get-service
$procs | Export-Csv test.csv -NoTypeInformation

Start-Job -ScriptBlock $sb -Name test


I was collecting some data and outputting a CSV.  My problem was more complex but this stands as a good example


I didn’t get the data I wanted


Thinking about it I put the full path to where I wanted the CSV


$sb = {
$procs = get-service
$procs | Export-Csv C:\MyData\scripts\Test\test.csv -NoTypeInformation

Start-Job -ScriptBlock $sb -Name test


And it works.


So where did my data go in the original version?


I ran this


$sb = {

$procs = get-service
$procs | Export-Csv test.csv -NoTypeInformation

Start-Job -ScriptBlock $sb -Name test


And then pulled the data from the job


£> Receive-Job -Id 10



Obvious really – a job runs in a new powershell process that doesn’t run your profile so it starts in the default location  - which is your home directory. And sure enough the CSV file is there


£> ls C:\Users\Richard\Documents\*.csv

    Directory: C:\Users\Richard\Documents

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        14/09/2014     11:50      46042 test.csv


I can’t remember how many times I’ve told people that PowerShell jobs run in a separate process so I should have realised.  Excellent example of the more you know the more you need to learn

Grains of rice on a chess board

There is a story about the inventor of chess being rewarded by putting 1 grain of rice on the first square of the board; 2 on the second and so on.  How much rice does that come to?


The total number of grains is 1.84467440737096E+19


At 25mg per grain thats 461168601842739 kilogrammes of rice


Which in more understandable terms is:


461,168,601,842.739 metric tonnes



453,886,749,619.642 tons


That’s a lot of rice


If you want to play around with the numbers the calculations are:


[double]$result = 0

1..64 |
foreach {
$square = [math]::Pow(2, ($psitem -1))
$result += $square


$wt = 0.025

$totalweight = ($wt * $result)/1000

$mtwt = $totalweight /1000

$tons = $totalweight * 0.00098421

Event Log Providers

An event log provider is writes to an event log.  I’ve used WMI in the past to get these but while looking for somethign else discovered that Get-WinEvent can also find this information


Get-WinEvent -ListProvider * | ft Name, LogLinks -AutoSize –Wrap


Provides a nice long list of all of the providers and the event logs they write to.


Usually I’m only interested in what’s writing to a particular event log. And that’s where things get a bit more messy.


The loglinks are supplied as a System.Collections.Generic.IList[System.Diagnostics.Eventing.Reader.EventLogLink] LogLinks  object that doesn’t play nicely with –in or –contains


So we need a bit of PowerShell manipulation to get what we want


$log = 'System'

Get-WinEvent -ListProvider * |
foreach {
if ($log -in ($psitem | select -ExpandProperty Loglinks | select -ExpandProperty Logname)){
    New-Object -TypeName psobject -Property @{
      Name = $psitem.Name
      Log = $log


The trick here is that the loglinks are a collection of objects so you need to expand them twice to get to the name.  Not pretty but it works

Count property

Its frequently said that PowerShell is so big that no one can know everything about it.  I proved that today when I “discovered” a change in PowerShell of which I wasn’t aware.


If you create an array:

£> $a = 1,2,3

You can then get the number of members of that array i.e. its length


£> $a.count


£> $a[0]


In PowerShell 1.0 and 2.0 if you tried that on a variable that only held a single value you would get an error when you tried to access the first value:

£> $b = 1

£> $b.count

The count property returns nothing


£> $b[0]
Unable to index into an object of type System.Int32.
At line:1 char:4
+ $b[ <<<< 0]
    + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : CannotIndex


This changed in PowerShell 3.0 and later

£> $b = 1
£> $b.count

£> $b[0]


You can even try other indices
£> $b[1]


And just get nothing back rather than an error.


This is really useful as you can now safely test on the Count property and if the value is greater than 1 to determine if its a collection.  Alternatively always treat it as a collection and iterate over the number of elements.  I can see this simplifying things for me in quite a few situations


I was asked about foreach today and responded with a description of who foreach-object works. Thinking about it I should have realised that part of the issue with foreach is the confusion that arises between foreach and foreach - -  that is the difference between the foreach PowerShell statement and the foreach alias of the foreach-object cmdlet.


To unravel the confusion there are two different things referred to as foreach. The confusion is that they do very similar things but are used in different ways.


The first is the PowerShell statement which is used to step through each value in a collection of values:


$procs = Get-Process

foreach ($proc in $procs) {

New-Object -TypeName PSObject -Property @{
   Name = $proc.Name
   SysMen =  $proc.NonpagedSystemMemorySize + $proc.PagedSystemMemorySize64



You create your collection of objects and then use foreach to step through them. It is convention to make the collection plural and the individual member of the collection its singular.  Within the script block you can define what happens to the object.


I know I could have a performed this action is a simpler way but I wanted to demonstrate how foreach works. The simpler way would be:

Get-Process |
select Name,
@{Name = 'SysMen';
Expression = {$_.NonpagedSystemMemorySize + $_.PagedSystemMemorySize64}}


Now we’ve got that out of the way what about the other foreach which is the alias of foreach-object.  This can be use to iterate over a collection of objects. The main difference is that the objects are usually piped into foreach:


Get-Process |
foreach {

New-Object -TypeName PSObject -Property @{
   Name = $_.Name
   SysMen =  $_.NonpagedSystemMemorySize + $_.PagedSystemMemorySize64



If you don’t like using $_ to represent the object on the pipeline try

Get-Process |
foreach {

New-Object -TypeName PSObject -Property @{
   Name = $psitem.Name
   SysMen =  $psitem.NonpagedSystemMemorySize + $psitem.PagedSystemMemorySize64



which is exactly equivalent to

Get-Process |
ForEach-Object {

New-Object -TypeName PSObject -Property @{
   Name = $psitem.Name
   SysMen =  $psitem.NonpagedSystemMemorySize + $psitem.PagedSystemMemorySize64



Using the cmdlet or its alias you can set up script blocks to process once when the first object reaches foreach (BEGIN), once per object on the pipeline (PROCESS) and once when the last object has been processed (END)

Get-Process |
ForEach-Object `
  Write-Host "First object about to be processed"
} `
New-Object -TypeName PSObject -Property @{
   Name = $psitem.Name
   SysMen =  $psitem.NonpagedSystemMemorySize + $psitem.PagedSystemMemorySize64
-END {
Write-Host "Last object processed"


Your ouput looks like this


First object about to be processed

Name                                                                      SysMen
----                                                                      ------
armsvc                                                                    164096
concentr                                                                  200400
conhost                                                                   119104
csrss                                                                     153664
csrss                                                                     407760

WUDFHost                                                                  103696
WWAHost                                                                   778816
WWAHost                                                                   785120
Yammer.Notifier                                                           566304
Last object processed


More info is available in the help files for foreach-object and about_foreach

Can it -whatif

One of the nice things about PowerShell is that it can help you prevent mistakes. Many of the cmdlets that make changes to you system have a –whatif parameter that allows you to test your actions:


£> Get-Process | Stop-Process -WhatIf
What if: Performing the operation "Stop-Process" on target "armsvc (1564)".
What if: Performing the operation "Stop-Process" on target "audiodg (3004)".
What if: Performing the operation "Stop-Process" on target "concentr (7080)".
What if: Performing the operation "Stop-Process" on target "conhost (3628)".





The –whatif parameter is only present on cmdlets that make changes and then only if the team writing the cmdlet implemented it – they should but you can’t guarantee it happened. So how can you find out which cmdlets implement –whatif?


Use Get-Command


Compare these 2 commands.


£> Get-Command -Module CimCmdlets | select Name



shows the cmdlets in a module


£> Get-Command -Module CimCmdlets -ParameterName Whatif | select Name



Now you can test a module to see which cmdlets have –whatif enabled.  You can also test at just the cmdlet level:

£> Get-Command *process -ParameterName Whatif  -CommandType cmdlet | select Name


£> Get-Command *wmi* -ParameterName Whatif  -CommandType cmdlet | select Name


Select-Object or Where-Object

Both Select-Object and Where-Object (referred to by their aliases of select and where from now on) are both used to filter data.


It is important to know the way these 2 cmdlets are used.


Where is used to restrict the objects on the pipeline to those where one or more properties satisfy the filter criteria e.g.

Get-Process | where CPU -gt 20


You get a reminder of this if you you use the full syntax

Get-Process | where -FilterScript {$_.CPU -gt 20}


As a matter of style you very rarely see anyone using the parameter name –FilterScript.


Select is used to cut the number of properties on an object to just those you want to work with e.g.

Get-Process | select Name, Id, CPU


If you want just those properties for the processes where CPU time is greater than 20 seconds you need to combine them on the pipeline:

Get-Process | where CPU -gt 20 | select Name, Id, CPU


There isn’t a way to embed a where type filter in a select or vice versa. Keep it simple. Use the pipeline and let the cmdlets do the job for which they were designed.

Invoke-Item tips

Invoke-Item is another cmdlet that you don’t see used that often but there is one place where its invaluable – opening files. If you give Invoke-Item the path to a file

Invoke-Item -Path .\procs.txt


The file will be opened with the default application associated with that extension. In this case Notepad.


If you use a PowerShell script file

Invoke-Item .\t1.ps1


It will be opened in Notepad for you to examine.  CSV files automatically open in Excel and other Office files are opened in the correct application.


Using the alias ii for Invoke-item makes it even easier.