I just posted an article to the FAQ on the PoshCode Wiki answering this question that came up again on our IRC PowerShell user group today. It came up in the context of determining whether a function was the last function on the pipeline, because one of our users was looking for a way (other than creating ps1xml files) to output objects onto the pipeline for use in other functions, but still format those objects nicely if they were output directly.
Before I give the solution, I just want to say: don’t change your output based on where you are in a pipeline.
There are numerous scenarios where your function will be the last one on a pipeline, but still be participating in further pipelines, including formatting and output modification. For example, take our function Test-Pipeline (defined below) in these three scenarios below. In none of these scenarios would it be appropriate for the function to write formatted output instead of outputting the raw object, but in each case, the function is the last function in the pipeline.
However, if you want to determine your function’s position in the pipeline for some other reason, the answer is simple. You need to use $MyInvocation and compare the PipelineLength and PipelinePosition properties:
One of the consistent questions about PowerShell is: what’s the best way to write a script or a function to process pipeline objects and be able to take it’s parameters as a normal function?
The first thing to know is that in PowerShell, there’s really no difference between a script (just a file with a .ps1 ending) and a function as you’ll see written below. If you take a function Get-Square, and remove the first and last lines ( Function Get-Square { … } ) you could put them in a file called “Get-Square.ps1” in your PATH, you use them exactly the way you would the function that was pre-loaded into memory by dot-sourcing or pasting it on the command line. Of course, doing that easily requires writing the function parameters on their own line using PARAM(...) syntax, which is why I recommend doing that.
When your script or function is used on the pipeline its begin block is called once when the pipeline starts up, and then the process block is called repeatedly: once to process each pipeline object, and finally, the end block is called after all the objects have been processed through the whole pipeline. If you don’t understand that, you should play with this function, try calling it by passing a series of numbers through multiple instances of it:
Incidentally, if you don’t specify the begin, process, or end blocks, the body of your function is treated as the end block. This is so that you can use the special $Input variable, which collects all the things passed in on the pipeline, and thus only works in the end block. That would allow the following function to behave the same way regardless of whether it was invoked on the pipeline or by passing an $InputObject. Notice, however, the difference between this, and the function above.
The basic idea here is to rewrite that function such that it can be used to process a set of numbers from either the pipeline or an argument, without interfering with the processing of other parameters. We require that the function process items as they come in, rather than waiting until it’s received all input before processing them the way Test-SquareEnd does.
In the current CTP 3 of PowerShell 2, you just specify ValueFromPipeline=$true for the parameter you want to set from the pipeline, and the function will work the same way whether you pass the numbers as a parameter or along the pipeline — you can even control which attribute of the objects on the pipeline will be used, but that’s a whole other article.
Of course, PowerShell 2.0 is still in beta status, and even after it’s released you may need to write scripts that are backwards compatible to PowerShell 1.0, and the excercise of doing so may help you to understand more about how PowerShell functions work, and particularly how they behave in the pipeline.
Since in a PowerShell 1.0 function it’s not really supported directly, we need to do some extra work:
Of course, this is just an example method, squaring things isn’t that exciting — but what’s special about it is that the output is almost exactly the same whether you call it with parameters Get-Square 1,2,3,4 or on the pipeline: 1,2,3,4 | Get-Square.
The trick is that it actually executes the same way in either case:
$InputObject), it calls itself and passes those values on the pipeline.$_ is set, and the process block is executed.There is one tiny difference in the processing, which in real-world use is practically never noticeable (you can see it in our example if you call it like this):
You’ll see that unlike the PowerShell 2.0 pipeline function, when you pass the numbers as a parameter, the first one of them actually gets passed through the process block before the begin block of the second function on the pipeline is called. This usually doesn’t have any effect, but it’s something to keep in the back of your head.
$null can cause problems.begin block, you should only do so in an ELSE case: when $InputObject is null. That way, the code will only execute once each time you call the function. end block: you have to keep your code in an If(!$InputObject) block to avoid executing it twice (when you pass the values as an argument, and it re-invokes itself). process block must wrap all of your process block code, so that you don’t process the arguments twice, and you shouldn’t refer to $InputObject, but instead should use the automatic $_ variable which is the value passed when the function is (re)invoked via the pipeline.Here’s some sample output, in case you’re wondering:
I’ve updated the PoshCode script module to support CTP3, and added a -limit parameter to the Get-PoshCode cmdlet so you can specify how many items you want retrieved in the case where there are a lot of matches for your search terms — by default the limit is 25.
You’ve always been able to pass a LIST parameter to the API, and get more results by specifying a higher number. But it never worked with the “path” notation (until now).
That is, you used to be able to do:
To make the API a little easier to use I’ve enhanced it just now:
So, you can use any of these URLs:
There are a lot of search results for “start” ... feel free to play with enhancing the PoshCode module, or incorporating this into your apps, etc.
[new] I should add that you don’t have to specify the limit or page number. By default you’ll get the first 10 items, which should be enough.
A relatively new PowerShell user came into #PowerShell on IRC.FreeNode.net this week to ask a question about scripts and the pipeline, and the conversation went so well, that I thought I’d share it with you all in case it helps clear things up for you. We’ll call him “user” since he left before I could get his permission to paste this, and I’ve cleaned up, reordered and in a couple of cases added lines to make the conversation seem a little more linear than the chat really was.
user: Can you explain the distinction between $_ and a param in a script context?
Jaykul: I sure can. And I think I can do it simply … take an example script|function … actually, we’ll just use a scriptblock
Jaykul: it can have pipeline blocks, or not (the pipeline blocks are the BEGIN{...} PROCESS{...} END{...})
user: yeah, I’m with you
Jaykul: so if you pass a value via an argument, it’s present in all three blocks, and has the same value the whole time (unless you change it in your script)
user: really
Jaykul: But when you put that script in a pipeline, your BEGIN {} block is called first, and then for EACH item in the pipeline, powershell sets $_ and calls your PROCESS block, and finally, your END{} block is called after all pipeline items have been processed
Jaykul: Here, check this out (run it so you can see) Read the rest of this entry »
I wrote a post last week about how to write functions for use in the PowerShell pipeline and I’ve been using the template I wrote in that post as the basis for several of my other scripts … and I’ve been gradually fleshing it out, and improving it, so I thought I’d drop it here with all of it’s inline comments. Hopefully that will be better than what you would get if I just trying to explain it in a blog post
.
Incidentally, this script is also on PowerShellCentral scripts Repository where I will probably post any future modifications … Read the rest of this entry »
[new] Update, I created a better version of a pipeline function for powershell …
Every once in a while the question of how to best use the process block of a function to process pipeline objects comes up on IRC, and although I’m sure others have already written this up on the web in the past, we’ve been polishing up this example sending it back and forth to each other for a couple of weeks, and it seems to me it’s about time to publish it a little more prominently.
The basic idea was to write a function that could be used to process a set of inputs from either the pipeline or an argument, while still allowing other arguments to be passed and processed. This is something that’s very easy for a cmdlet, because you can specify that a certain parameter will receive pipeline input (and even control which attribute of the objects on the pipeline will be used), but in a script it’s not really supported. In addition, we require that the function process items as they come in, rather than waiting until it’s received all input before processing them.
This is just an example method, it doesn’t really do much, except that it outputs exactly the same thing whether you call it like Test-Pipeline -inputObject "Foo","Bar","Baz" "Kill Each Process" or like: ""Foo","Bar","Baz" | Test-Pipeline "Kill Each Process".
It works by calling itself with the inputObject argument passed on the pipeline when you specify it as an argument, so that code is processed exactly right. This means that the first line you see in the process block must remain the first line, even if you add additional code to the block.
We use a trick to make sure that additional unnamed parameters work normally by using a switch parameter -inputObject followed by a second parameter $io which we treat as inputObject if inputObject is set. This works because switch parameters don’t expect values, so we’re able to sort-of hijack it to emulate the cmdlet behavior. Note that if it is not set, we move the value of the $in parameter into the $args array which contains any additional parameters. This arrangement allows it to behave rather like a cmdlet would, but it’s a little delicate: if you’re going to pass input objects, you must specify the switch as the first argument, and pass the $io (inputObjects) immediately following (as though they were the value of the inputObjects parameter). All of that is necessary so that we can optionally pass additional parameters.
You can also add additional named parameters — but you would need to either always specify their names when you called them, or carefully adjust the code so that they get shifted if -inputObject isn’t specified.
Here’s some sample output, in case you’re wondering:
I wanted to officially acknowledge that /\/\o\/\/ and Gaurhoth and Oisin and Brandon have all contributed to the polishing and testing of this script in various forms … thanks to all
Someone asked a question in the #PowerShell channel on irc.FreeNode.net today about how to use filters, and pasted this an example like this:
The question was: why doesn’t this have any output? Well, the answer is: it can’t have any output. The filter outputs true or false for each item passed into it, but the where scriptblock doesn’t actually pass anything into the filter! Of course we quickly fixed his problem the right way by rewriting the filter to output the items, and getting rid of the where-object call completely … but when I started wondering where he had gotten the bizarre idea to use a filter like that, his answer was: “I read about_filter.” So I went and looked, and sure enough, the example pasted above is straight out of about_filter. And it’s as wrong as it can possibly be.
Let me fix it, and then offer some clarification below. In about_filter there is an example like this: Get-Process | where {$_.processname -like "[a-m]*"} and then a table describing each element of the Where-Object command, and just below it, there is some text about the filter command. Mentally replace that text with this: Read the rest of this entry »