Piping and the Pipeline
It’s inevitable: no sooner do you get Windows PowerShell installed then you start hearing about and reading about “piping” and “the pipeline.” In turn, that leads to two questions: 1) What’s a pipeline?, and 2) Do I even need to knowabout piping and pipelines?
Let’s answer the second question first: Do you even need to knowabout piping and pipelines? Yes, you do. There’s no doubt that you can write Windows PowerShell scripts without usingpipelines. The question, however, is whether you’d wantto write PowerShell scripts without using pipelines. You can order a banana split and ask them to hold the ice cream; that’s fine, but at that point you don’t really have a banana split, do you? The same is true of the PowerShell pipeline: you can write scripts without a pipeline. But, at that point, do you really have a PowerShellscript?
Note. OK, we’re exaggerating a little bit: some scripts don’t needa pipeline. In general, however, that’s not going to be true of longer and more complicated PowerShell scripts.
Assembling a Pipeline
So then what ispiping and the pipeline? To begin with, in some ways the term “pipeline” can be considered something of a misnomer. Suppose you have an oil pipeline, like the Alaska Pipeline. You put oil in one end of the pipeline; what do you suppose comes out the other end? You got it: oil. And that’s the way a pipeline is supposed to work: it’s just a way of moving something, unchanged, from one place to another. But that’s not the way the pipeline works in Windows PowerShell.
Note:There’s some debate about this. For example, when you “pipe” an object from one part of a command to another, you’re simply passing that object – unchanged – from one part of the command to another. There have been some interesting discussions around the scripting water cooler about this very topic that involve Kool-Aid and waste management facilities (don’t ask), as well as mentions of marketing pipelines and other industry-specific terminology, but we won’t confuse you with all that. You’re probably confused enough already, and since our goal is to un-confuse you, we’ll move on.
Instead, think of the Windows PowerShell pipeline as being more like an assembly line. With an assembly line you start with a particular thing; for example, you start with a car. However, you don’t start with a finishedcar; instead, you start with a sort of framework that will eventually bea car. As the car travels down the assembly line it passes through various stations; at each station workers make some sort of modification, welding on doors, adding windows, installing seats. When you’re all done you’ll still have a car; you won’t have a tube of toothpaste or a barrel of oil. But thanks to all the changes that were made along the way, you’ll have a very different car than the “car” you started with.
Start with a Cmdlet
A similar process takes place when you use the pipeline in Windows PowerShell. For example, suppose there happened to be a cmdlet named Get-Shapes; when you run this cmdlet it returns a collection of all the geometric shapes found on your computer. To call this hypothetical cmdlet you use a command similar to this:
Get-Shapes
In return, you get back a collection like this one:
The Filtering Station
That’s pretty cool – except for one thing. As it turns out, we’re only interested in the orangeshapes. Unfortunately, though, our hypothetical Get-Shapes cmdlet doesn’t allow us to filter out items that fail to meet specified criteria. Oh, well; guess we’re out of luck, right?
Right.
No, wait, we mean wrong. Granted, Get-Shapes doesn’t know how to filter out unwanted items. But that’s not a problem, because PowerShell’s Where-Objectcmdlet doesknow how to filter out unwanted items. Because of that, all we have to do is use Get-Shapes to retrieve all the shapes, then hand that collection of shapes over to Where-Object and let itfilter out everything but the orange shapes. In other words:
Get-Shapes | Where-Object
{$_.Color –eq "
Don’t worry about the syntax of the Where-Object cmdlet for now; for an overview of using Where-Object you can see this article . The important thing to note for now is the pipe separator character (|) that separates our two commands (Get-Shapes and Where-Object). When we use the pipeline in Windows PowerShell that typically means that we use a cmdlet to retrieve a collection of objects. However, we don’t do anything with those objects, at least not right away. Instead, we hand that collection over to a second cmdlet, one that does some further processing (filtering, grouping, sorting, etc.). That’swhat the pipeline is for.
And in our hypothetical example, the pipeline provides a way for us to filter out everything except the orange-colored shapes:
The Sorting Station
That’s cool, but
what’s even cooler is the fact that you aren’t limited to just two
stations on your assembly line. For example, suppose we want to
sort the orange shapes by size. Where-Object doesn’t know how to
sort things. But
Sort-Objectdoes:
Get-Shapes | Where-Object
{$_.Color –eq "
Does this really
work? Of course it does:
A Real-Life Pipeline
Here’s a somewhat
more practical use of the PowerShell pipeline. The command we’re
about to show you uses the
Get-ChildItemcmdlet to retrieve a list of all the items
found in the folder C:\Scripts. The command then hands that
collection over to the Where-Object cmdlet; in turn, Where-Object
filters out any item (file or folder) that is less than 200 KB in
size. After it finishes filtering, Where-Object hands the remaining
items over to the Sort-Object cmdlet, which sorts those items by
file size.
The command itself
looks like this:
Get-ChildItem C:\Scripts |
Where-Object {$_.Length -gt 200KB} | Sort-Object Length
And when we run the
command we get back something along these lines:
Directory:
Microsoft.PowerShell.Core\FileSystem::C:\Scripts
That’s pretty slick,
but some of you seem a little skeptical. “OK, that
isnice, but it’s not that big of a deal,” you say. “After
all, if I write a WMI query I can do filtering right in my query.
And if I write an ADSI script I can add a filter that limits my
collection to, say, user accounts. I’m already doing all this
stuff.”
Depending on how you
want to look at it, that’s true; after all, you
canuse filtering in either a WMI or an ADSI script. However,
the approach used when writing a filter in WMI is typically very
different from the approach used when writing a filter in ADSI. In
turn, an ADSI filter is different from the approach used when
writing a filter using the FileSystemObject. The advantage to
Windows PowerShell, and to using the pipeline, is that it doesn’t
matter what kind of data or what kind of object you’re working
with; you just hand everything off to Where-Object and let
Where-Object take care of everything.
Or take sorting, to
name another commonly-used operation. If you’re doing a database
query (including
Is that the case in
PowerShell? You already know the answer to that, don’t you? Of
course that’s not the case; in PowerShell you just pipe your data
to the Sort-Object cmdlet, sit back, and relax. For example, say
you want to retrieve information about the services running on a
computer, then sort the returned collection by service status
(running, stopped, etc.). Okey-doke:
Get-Service | Sort-Object
Status | Format-Table
Note. You might note that as a bonus we took the sorted data
and piped it to the
Format-Tablecmdlet; that means the final onscreen display
ends up as a table rather than a list.
Don’t Get Carried Away
Yes, this
iseasy isn’t it? In fact, about the only time you’ll ever
run into a problem is if you get carried away and try pipelining
everything. Remember, you can’t pipeline something unless it
makes sense to use a pipeline. It makes sense to pipeline service
information to Sort-Object; after all, Sort-Object can pretty much
sort anything. It also makes sense to pipe the sorted information
to Format-Table; after all, Format-Table can take pretty much any
information and display it as a table.
But consider this
command:
Sort-Object |
Get-Process
What’s this command
going to do? Absolutely nothing. Nor should we expect it to do
anything. After all, Sort-Object is designed to sort information
and there’s nothing here to sort. (Incidentally, that’s a hint that
Sort-Object should typically appear on the right-hand side of a
pipeline. You need to first grab some information and
thensort that information.)
Note. Are there exceptions to this rule? Sure. For example,
suppose you have a variable $a that contains a collection of data.
You can sort that data, and sidestep the pipeline altogether, by
using a command like this:
Sort-Object –inputobject
$a
Someday you might actually have to use an approach similar to this;
as a beginner, however, you shouldn’t worry about it. Instead, you
should get into the habit of using piping and the pipeline. Learn
the rules first; later on, there will be plenty of time to learn
the exceptions. But even if there
wassomething for Sort-Object to sort this command still
wouldn’t make much sense. After all, the Get-Process cmdlet is
designed to retrieve information about the processes running on a
computer; what exactly would Get-Process do with any sorted
information handed over the pipeline? For the most part, you first
acquire something (a collection, an object, whatever) and
thenhand that data over the pipeline. The cmdlet on the
right-hand side of the pipeline will then proceed to do some
additional processing and formatting of the items handed to it.
As we implied above,
when you
dohand data over the pipeline make sure there’s a cmdlet
waiting for it on the other side. The more you use PowerShell the
more you’re going to be tempted to do something like this:
$a = Get-Process |
$a
Admittedly, that
looksOK – it looks like you want to assign the output of
Get-Process to the variable $a then display $a. However, it’s not
going to work; instead you’re going to get an error message similar
to this
Expressions are only
permitted as the first element of a pipeline.
We’ll concede that
this can be a difficult distinction to make, but pipelines are used
to string multiple commands into a single command,
with data being passed from one portion of the pipeline to the
next. Furthermore, as that data gets passed from one section to
another it gets transformed in some way: filtered, sorted, grouped,
formatted, whatever. In the invalid command we just showed you,
we’re not passing any data. We’ve really got two totally separate
commands here: we want to use Get-Process to return information
about the processes running on a computer and then, without
transforming that data in any way, we want to display the
information. Because we really have two independent commands, we
need two lines of code:
$a = Get-Process
All right, if you’re
bound and determined to do this all on a single line of code,
separate the commands using a semicolon rather than the pipe
separator:
$a = Get-Process;
$a
But this isn’t
pipelining, this is just putting multiple commands on one line.
Bonus Tip
OK, but suppose you
wanted to get process information, sort that information by process
ID, and then – instead of displaying that information – store the
data in a variable named $a. Can you do that? Yes you can, just
like this:
$a = (Get-Process |
Sort-Object ID)
What we’re doing
here is assigning a value to $a. Which value are we assigning it?
Well, we’re assigning it the value we get back when we call the
Get-Process cmdlet and then pipe the returned information to
Sort-Object. This command works because we put parentheses around
our Get-Process/Sort-Object command. Any time PowerShell parses a
command, it carries out the instructions enclosed in parentheses
before it does anything else. In this case, that means PowerShell
first gets and sorts process information, then assigns that data to
$a. Display the value of $a and see for yourself.
But if you’re a
beginner, don’t worry too much about this bonus example. Get used
to using pipelines in the “traditional” way, then come back here
and start playing around with parentheses.
More on Pipelining
With any luck, this
should be enough to get you started with piping and pipelines. Once
you get comfortable with the basic concept you might want a little
more technical information about pipelines. If so, just type the
following from your Windows PowerShell command prompt:
Get-Help
About_pipeline
Or, even better,
download our new
Windows PowerShell Graphical Help Fileand read the same help
topic in the comfort of a .CHM help file. Either way, don’t assume
that you can ignore pipelines and become a true Windows PowerShell
scripter; you can’t. You can – and should – ask them to hold the
maraschino cherry when ordering a banana split. But if you ask them
to hold the ice cream, you’ll spend all your time wondering why
people make such a big deal about banana splits. Don’t make that
same mistake with Windows PowerShell.
Mode
LastWriteTime Length Name
----
------------- ------ ----
-a---
2/19/2007 7:42 PM 266240
scores.mdb
-a---
5/19/2007 9:23 AM 328620
wordlist.txt
-a---
12/1/2002 3:35 AM 333432
6of12.txt
-a---
5/18/2007 8:12 AM 708608
test.mdb
At line:1 char:21
+ $a = Get-Process | $a
<<<<
$a