Let's start to pipe

Small ist beautiful, and pipes makes our small commands to a beautiful whole:

> ps -ef | grep java

So what does the pipe do? It gets the standard output of the first command as a text stream and feeds the standard input of the following command with the stream. PowerShell supports pipes too. And if you do help Select-String, you can read that Select-String does the same as grep: it selects lines from a text stream matching a pattern. So let's give it a try. First we look at some lines of the output of Get-Process (the dots are placeholders for more lines):

> Get-Process
1575      79    64728      36472   334   197,22   1396 explorer
 434      71   207576     183404   467   116,31   4524 firefox
   0       0        0         24     0               0 Idle
 353      32   148636      65352  1327     1,56   4756 java
 110      11     4856       3132    43            1940 LMS

Now let's grep:

> Get-Process | Select-String java

System.Diagnostics.Process (java)

Ooops! That is not what we expected. The result says something about java, but it is definitely not the line from above. So what happened?

Object Orientation in PowerShell

The answer to the question is that cmdlets in PowerShell don't output streams of text, but streams of objects (or a single object). To be more specific: the output consists of one or more .NET objects. So if Get-Process delivers objects, why do we see a stream of text, the list of all processes with some additional information, on the screen? It is because the output of the final cmdlet in a pipe (or a single cmdlet without a pipe) is automatically piped to the cmdlet Out-Default, which renders the output to a text stream with the default formatting of the PowerShell formatting system. The definition of how to format specific classes (object-types) is given in a number of XML files in the installation directory of PowerShell. So you can change it, or add definitions for more classes. What if there is no definition for a given object? Then PowerShell uses the default formatters Format-Table, if there are few attributes, or Format-List, if there are many. These formatters simply output the values of all properties, either as a table or as a list. Of course, this automatical formatting does not take place, if the final output already is a text stream.

But still we have no answer why Get-Process | Select-String java returned System.Diagnostics.Process (java). The reason is, that Select-String expects a stream of text (or a string) as input, but gets a stream of objects. So it converts each object to a string before processing. The .NET way to convert an object to a string is to to call the method toString() on the object (just like in java). And this method gives the result shown above.

If you really want the UNIX behaviour of greping the output of Get-Process as a stream of text you can use:

> Get-Process | Out-String -stream | Select-String java
     341      31   147300      70492  1327     1,65   4756 java

This solution looks overly complicated and akward to the way we did it in bash. And it is. Flatting out all objects and converting everything to a stream of text for processing is against the concept of object orientation in PowerShell. The PowerShell way to do such processing in a pipe is to use the properties of the objects. To grep the java process, you could do something like:

> Get-Process | Where-Object { $_.ProcessName -match "java" }
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
341      31   147300      70516  1327     1,65   4756 java

Where-Object executes a block of code (in curly braces) for each object in the pipe. $_ represents the current object. You can use any attribute or method defined on $_. The operator -match compares two strings and returns true, if the first string contains the second. If you need finer control about the comparison, you could use the [regex] .NET class. You can find more info on operators in the PowerShell help system: Get-Help about_operators -full. By the way: Finding a process by name could have been accomplished in Powershell without using any pipes:

> Get-Process -name java

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
  358      33   149288      65036  1330     1,39   4452 java

Finding out more about an object

So obviously in PowerShell you need to work a lot with objects, and to do so you must know, which properties and methods an object has. In the example above, how could you know, that the name of the process is stored in the property ProcessName? There are several ways to achieve this. First you can get the type of any .NET object by calling the method getType() on it. This is similar getClass() in java. The result of getType() is an object of class System.Type, containing all relevant type information. To find the full class name of an object returned by Get-Process you can do:

> (Get-Process)[0].getType().FullName

Since Get-Process returns an array of objects, you have to select the first object, by using the [0] operator, otherwise you would get the type of the array itself. Now that you know the full class name, you can look it up in the documentation. Another way would be to make a deeper inquiry into the System.Type object, eg: (Get-Process)[0].getType().getMethods(). But the easiest way is to use the cmdlet Get-Member:

> (Get-Process)[0] | Get-Member
PrivateMemorySize          Property       System.Int32 PrivateMemorySize {get;}
PrivateMemorySize64        Property       System.Int64 PrivateMemorySize64 {get;}
PrivilegedProcessorTime    Property       System.TimeSpan PrivilegedProcessorTime {get;}
ProcessName                Property       System.String ProcessName {get;}
ProcessorAffinity          Property       System.IntPtr ProcessorAffinity {get;set;}
Responding                 Property       System.Boolean Responding {get;}
SessionId                  Property       System.Int32 SessionId {get;}
Site                       Property       System.ComponentModel.ISite Site {get;set;}
StandardError              Property       System.IO.StreamReader StandardError {get;}
StandardInput              Property       System.IO.StreamWriter StandardInput {get;}
StandardOutput             Property       System.IO.StreamReader StandardOutput {get;}
StartInfo                  Property       System.Diagnostics.ProcessStartInfo StartInfo {get;set;}
StartTime                  Property       System.DateTime StartTime {get;}
PSConfiguration            PropertySet    PSConfiguration {Name, Id, PriorityClass, FileVersion}

In the (incomplete) list of members above, you can see a property set called PSConfiguration. You will not find this property set in the .NET documentation of the class System.Diagnostics.Process. Why? Powershell sometimes modifies objects with the so called Extended Type System (ETS). It adds members to give more information. It hides members that are useless in PowerShell. The name of added members usually start with the prefix PS.

The method toString() often returns the class name of an object. We have seen this in the initial example where we tried to grep java from the list of processes in a naive way.


Just like in UNIX shells you can use variables in PowerShell. The main difference is that in PowerShell variables hold objects. So if a pipe gets too long and complicated you can store intermediate results in variables.

> $p = Get-Process
> $p.getType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

> $j = $p | Where-Object {$_.ProcessName -match "java" }
> $j.getType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    Process                                  System.ComponentModel.Component

> $j.Kill()

Above you can see that it is possible to start a pipe with a variable containing an object to process the content of the variable. Another thing to note is that there is no output on the screen, if you assign the result of a pipe to a variable. With the final line I killed the java process. To get a list of all variables set, you can use Get-Variable. Get-Help -full about_variables gives you more info on PowerShell variables.

Get in touch