The PowerShell pipeline
123Nested pipelines
When pipeline input won’t do what you need, a parenthetical command often will. Alternatively, fall back on using Foreach-Object to work with one or more commands, or even a complete pipeline of commands, for each object passing along the pipeline.
8.6
Nested pipelines
The pipeline is viewed as a sequential process: A | B | C | D. Sometimes you may need to reuse the values from B in later parts of the pipeline. This can be difficult to achieve and usually involves nested pipelines and using variables to store data.
Imagine you need to print out the multiplication tables for the values 1 to 10. You could do something like this:
1..10 |
Foreach-Object -Process { $value = $psitem
1..10 | ForEach-Object -Process { "$value * $($_) = " + ($value * $_)} }
Put the values 1 through 10 onto the pipeline. In the Process block of ForEach- Object, the current pipeline object ($psitem - introduced in PowerShell v3 as an alternative to using $_) is put into the variable $value. A second pipeline has the val- ues 1 to 10 placed in it and each is multiplied by $value with the results written to screen. The output would look like this:
1 * 1 = 1 1 * 2 = 2 1 * 3 = 3 ... 10 * 9 = 90 10 * 10 = 100
PowerShell v4 introduced a new common parameter, PipelineVariable, that’s designed for these cases. The parameter has an alias of PV. You can discover this example in the help file about_CommonParameters:
1..10 |
Foreach-Object -PipelineVariable Left -Process { $_ } | Foreach-Object -PV Right -Process { 1..10 } |
Foreach-Object -Process { "$Left * $Right = " + ($Left * $Right) }
Using the range operator, integers 1 through 10 are placed on the pipeline. In the first Foreach-Object call, the values are put into the $Left variable. The pipeline pro- gresses to the second Foreach-Object, where for each object on the pipeline the val- ues 1 through 10 are added to the $Right variable.
In the third Foreach-Object, the value of $Left is multiplied by the correspond- ing value of $Right to produce output similar to the previous version of the code.
NOTE Each value of $Left is multiplied by every value in $Right.
As a more practical example, consider the need to create a folder hierarchy. You need 10 folders called ServerN, where N is a value 1 through 10. Each of those folders
will have 10 subfolders, called Log1 to Log10. You can generate the folder structure like this:
$folders = 1..10 |
ForEach-Object -PipelineVariable TLfolder -Process {"Server$_"} | ForEach-Object -PipelineVariable ChildFolder -Process {1..10 | foreach
{"Log$_"}} |
ForEach-Object -Process {"$TLfolder\$childFolder"} New-Item -Path C:\ -Name "Logs" -ItemType Directory foreach ($folder in $folders){
New-Item -Path C:\Logs -ItemType Directory -Name $folder }
The folder names are generated by putting the values 1 to 10 onto the pipeline. The value is appended to “Server” and saved in the TLfolder variable. The pipeline pro- gresses, and in the second Foreach-Object, the values 1 to 10 are appended to “Logs” and saved to the PipelineVariable ChildFolder. The final Foreach-Object com- bines the contents of the two pipeline variables to produce the full folder name. New- Item is used to create a folder in the root of C:, and then foreach is used to iterate over the folder names to create the folder.
8.7
The pipeline with external commands
So if this pipeline binding stuff all relies on objects, what happens if you try to use external command-line utilities in PowerShell?
Simple: When a command-line utility, such as Ipconfig, is run in PowerShell, Pow- erShell captures its StdOut, which will contain text. Each line of text is brought into PowerShell as a String object. This usually works well, as long as you know what to expect. You’ll often pipe those String objects to a command like Select-String, which lets you search the strings for specific patterns.
Similarly, if you try to pipe objects to an external command-line utility, PowerShell will convert those objects to text (much as it does when creating an onscreen display for you to look at) and send that text to the external command’s StdIn. This rarely works well, except in cases where the external command doesn’t care about what the strings actually say. The More command—famous from the Dir | More example—is a command that works well with this technique. It doesn’t care what the strings say—it just displays them one page at a time and pauses until you press Enter to continue.
8.8
Summary
In this chapter, we’ve revealed one of the most important inner workings in Power- Shell. If your head is still spinning a bit, don’t worry—it’s taken us years to grasp and use the pipeline effectively. It’s something you should practice, and if you get stuck ask someone for help. Online forums like PowerShell.org, PowerShell.com, Scripting- Answers.com, and so on are all good places to ask questions, especially for tricky pipeline-binding problems.
125
Formatting
PowerShell, as you’ve learned in the preceding chapters, works primarily with objects. Objects are just an in-memory data structure. But the time comes when PowerShell needs to share information from those objects with us humans. Power- Shell has to take those in-memory data structures and convert them into something a person can view. PowerShell’s formatting system is designed to accomplish that task.
9.1
The time to format
Whenever you construct a command line, those commands all run in a pipeline. What you don’t see is an invisible cmdlet hanging out at the end of every pipeline: Out-Default. It’s hardcoded into the shell, and you can’t get rid of it. You also never have to explicitly call it. Its sole purpose is to kick off the formatting process, using whatever objects happen to be in the pipeline at that point.
This chapter covers
■ PowerShell’s formatting system
■ PowerShell’s Format cmdlets
■ Creating custom formats
That’s an important concept, so let’s linger on it for a second. Consider this com- mand line:
Get-Service | Export-CSV –Path services.csv
What output does that command create? You might be tempted to say “a file on disk,” but that’s completely external to PowerShell. Maybe a better question is “What objects does that command leave in the pipeline?” Try running the command, right now, and see what appears on the screen. The answer: nothing. Nothing appears on the screen, because no objects were produced by the command. Get-Service certainly produced objects and put them in the pipeline, but Export-CSV consumed those objects and didn’t put any of its own into the pipeline. So, no objects in the pipeline means Out- Default has nothing to work with, and so that’s what you see on the screen: nothing.
But let’s say you run a command that does leave objects in the pipeline. Those go to Out-Default, which simply passes them on to another cmdlet, Out-Host, that’s invoked by default. You don’t have to do anything. Out-Host can’t make heads or tails of those objects, though, because it only understands a special kind of object we call formatting directives (that isn’t their official name, but it’s one we use a lot because we don’t know if they even have an official name). So when Out-Host gets anything that isn’t formatting directives, it calls on the shell’s formatting system. That system kicks in, extracts data from the objects, and uses the data to create formatting directives.
Your Command Goes Here
Out-Default Out-Host Are these formatting directives? Create display according to formatting directives Formatting System No Yes
Figure 9.1 How PowerShell turns objects into text
127