• No results found

Understand the Pipeline Variable

Logical Operators

Task 2: Understand the Pipeline Variable

You may have noticed in previous examples when using the Where-Object Cmdlet you had a reference to $_. This is a special variable called the pipeline variable. The pipeline variable is used to refer to the current object on the pipeline.

An interesting fact about the pipeline is that, when looking at the data returned by a Cmdlet or function, objects are often returned one at a time. This operation is asynchronous in a way because a Cmdlet does not necessarily need to complete processing before the output can be passed on to the next command in the pipeline.

Some Cmdlets however, do require all of the possible input before they can produce any output, such as the case with Sort-Object. In the example above, Sort-Object needs to know the working set of every process before it can sort them according to that property.

1. When piping data, usually things will show up very quickly, which can make it seem like data gets sent along the pipeline instantly:

1..5

1..5 | Sort-Object -Descending

Here, you start by creating an array of numbers from one to five using the range operator (..). Since there are no additional commands on the pipeline, PowerShell outputs those numbers to the console. When you pipe that range of numbers to Sort-Object, it changes the order of the numbers as they get passed along the pipeline.

2. Using the ForEach-Object Cmdlet, you can slow down the data being sent along the pipeline.

1..5 | ForEach-Object { Start-Sleep 1; $_ }

The ForEach-Object Cmdlet instructs PowerShell to sleep, by using the Start-Sleep Cmdlet, for one second before then passing each object along the pipeline.

Get-Process Where-Object

Here you can see the numbers one through five displays with a one-second delay between each one. Each object gets passed along the pipeline and, in this case, it outputs them to the console.

3. However, not every Cmdlet can process the pipeline data asynchronously.

1..5 | ForEach-Object { Start-Sleep 1; $_ } | Sort-Object -Descending

Here you see a 5 second delay before anything actually happens because Sort-Object cannot process the data asynchronously. Sort-Object is a command that needs to receive all of the pipeline data before it can decide what the appropriate sort will be. After the initial delay, however, the numbers all appear almost instantly because Sort-Object sends all of the data along the pipeline once it is finished processing.

4. To slow down the data as it is passed further along the pipeline, you can move the ForEach-Object Cmdlet after the sort.

1..5 | Sort-Object –Descending | ForEach-Object { Start-Sleep 1; $_ }

Now you can see that the data is sorted first, quickly, before it is slowed down in the ForEach-Object loop before being output.

5. If you were to use the output of a Cmdlet rather than a range of numbers, you would see a similar result.

Get-Process | Sort-Object –Descending | ForEach-Object { Start-Sleep -m 250; $_ }

The process objects from Get-Process are passed along the pipeline one at a time. In this example, you can see each running process with a 250 millisecond delay between each one. In all the examples so far, you have been treating the pipeline variable as a single unit. However, in reality there are more behind the scenes.

6. Consider the members available to each object output by Get-Process.

Get-Process | Get-Member

You can see that there are several properties available. The pipeline variable applies to the entire object. In the case of Get-Member, it has to inspect each object to provide feedback to the user about the available members, since not every object on the pipeline will be of the same type. Get-Process contains the same type of object so all you see are members of the System.Diagnostics.Process type.

7. Now, consider a directory.

Get-ChildItem C:\Windows | Get-Member

In this case, you can see that there are both System.IO.FileInfo and System.IO.DirectoryInfo objects being passed along the pipeline.

8. Get-Member is another Cmdlet that processes data asynchronously.

Get-ChildItem C:\Windows\s*.*

When you run the command above, the Cmdlet returns directories before files in the list.

9. You can add a delay to this command before sending it along the pipeline to Get-Member.

Get-ChildItem C:\Windows\s*.* | ForEach-Object {Start-Sleep 1; $_ } | Get-Member

Now you will see the type information come back slowly. This is because Get-Member will only pass unique type information along the pipeline and, therefore, you have to wait for each of the objects and their associated sleep command to occur.

10. What you have seen is that $_ refers to only a single object along the pipeline. However, each object has individual members and the pipeline variable provides access to all of them for each individual object.

Get-ChildItem C:\Windows\s*.* | ForEach-Object { $_.Name; $_.LastWriteTime }

Here you can see that the ForEach-Object Cmdlet allows you to access the name and last write time for each object that is passed along the pipeline to it.