{"id":11531,"date":"2012-01-09T00:01:00","date_gmt":"2012-01-09T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2012\/01\/09\/increase-your-productivity-by-using-the-powershell-pipeline\/"},"modified":"2012-01-09T00:01:00","modified_gmt":"2012-01-09T00:01:00","slug":"increase-your-productivity-by-using-the-powershell-pipeline","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/increase-your-productivity-by-using-the-powershell-pipeline\/","title":{"rendered":"Increase Your Productivity by Using the PowerShell Pipeline"},"content":{"rendered":"<p><b>Summary<\/b>: Microsoft Scripting Guy, Ed Wilson, teaches you how to use the Windows PowerShell pipeline to increase your productivity and to avoid writing scripts.<\/p>\n<p><span><span><span><span><span><span><span><span><img decoding=\"async\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\" \/><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span> Hey, Scripting Guy! I keep hearing about the <i>pipeline <\/i>in Windows PowerShell, but I do not get it. I see commands, but I am not sure what they are actually doing. In addition, why do I need to use a pipeline<i> <\/i>in the first place? Surely, I can store stuff in variables, and use the <b>ForEach<\/b><i> <\/i>command to walk through collections. I just don&rsquo;t get it. I guess you can tell that I am an old VBScripter,<i> <\/i>and I guess old habits are hard to break. But really, what&rsquo;s up with the pipeline<i>?<\/i><\/p>\n<p>&mdash;DG<\/p>\n<p><span><span><span><span><span><span><span><span><img decoding=\"async\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\" \/><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span> Hello DG,<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Well, this new year is already shaping up to be an exciting one. The Scripting Wife and I are hard at work on a new series of Scripting Wife blogs in preparation for the 2012 Scripting Games. This year&rsquo;s games will be the biggest and the best ever. I have been talking to various presidents of Windows PowerShell Users Groups from around the world, and I plan to work with them to help them get their groups up to speed for this year&rsquo;s games.&nbsp;If you are not a member of a Windows PowerShell user group, check the Windows PowerShell Group website to see if there is one near you. If there is not one, and if you would like to start one where you live, contact me via the <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a> email address, and I will help point you in the right direction.&nbsp;<\/p>\n<p>Anyway, DG, you are not the first person to ask me about pipelines in Windows PowerShell. For people who approach Windows PowerShell from a strictly VBScript background, or even from a strict Windows background, the idea of a pipeline is somewhat foreign. At times, the process seems to work seamlessly, and at other times, it does not work at all. In some cases, the command appears to make sense, but in other cases, it does not make sense. And often there is neither rhyme, nor is there reason to the syntax and the commands themselves.<\/p>\n<h2>A basic example of a pipeline<\/h2>\n<p>A good way to see a pipeline in action is to create an instance of the <b>Notepad<\/b><i> <\/i>process, retrieve the newly created process, and stop that process. To start a new instance of the <b>Notepad<\/b><i> <\/i>process, I use the <b>Start-Process <\/b>cmdlet. I then use <b>Get-Process <\/b>to retrieve the process object, and I pipe the object to the <b>Stop-Process <\/b>cmdlet.<\/p>\n<p style=\"padding-left: 30px\">Start-Process notepad<\/p>\n<p style=\"padding-left: 30px\">Get-Process notepad | Stop-Process<\/p>\n<p>What happens under the covers is that the process object that is retrieved by the <b>Get-Process <\/b>cmdlet, is sent to the <i>InputObject <\/i>parameter of the <b>Stop-Process <\/b>cmdlet. The <b>Stop-Process <\/b>cmdlet then stops each process contained in the process object that comes across the pipeline. If we did not have the pipeline in Windows PowerShell, I would perform this operation, exactly the same as I did in the VBScript days&hellip;I would store the objects in a variable, and pass the variable to the <i>InputObject <\/i>parameter of the <b>Stop-Process <\/b>cmdlet. This technique is shown here:<\/p>\n<p style=\"padding-left: 30px\">Start-Process notepad<\/p>\n<p style=\"padding-left: 30px\">$notepad = Get-Process notepad<\/p>\n<p style=\"padding-left: 30px\">Stop-Process -InputObject $notepad<\/p>\n<h2>Examining the pipeline mechanics<\/h2>\n<p>I can confirm that the process object passes to the <i>InputObject <\/i>parameter by using the <b>Trace-Command <\/b>cmdlet.<\/p>\n<p><b>Note<\/b>: Microsoft MVP and <a href=\"http:\/\/blogs.technet.comhttps:\/\/devblogs.microsoft.com\/scripting\/honorary-scripting-guy-award-recipients-announced\/\" target=\"_blank\">Honorary Scripting Guy<\/a>, Don Jones, reminded me about using this cmdlet in his great column, <a href=\"http:\/\/www.windowsitpro.com\/blog\/powershell-with-a-purpose-blog-36\/windows-powershell\/figuring-pipeline-input-binding-tracecommand-141414\" target=\"_blank\">PowerShell with a Purpose<\/a>. (I had not played with it much since I wrote my book, <a href=\"http:\/\/www.amazon.com\/Windows-PowerShell-2-0-Best-Practices\/dp\/0735626464\/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1255959455&amp;sr=8-1\" target=\"_blank\">Windows PowerShell 2.0 Best Practices<\/a>)<i>. <\/i><\/p>\n<p>To use the <b>Trace-Command <\/b>cmdlet to display <i>ParameterBinding <\/i>information, I specify <i>ParameterBinding <\/i>to the <b>Name<\/b><i> <\/i>property&mdash;that is the name of the command that I want to trace. I use the <i>PSHost <\/i>switched parameter to tell the cmdlet to monitor the Windows PowerShell host, and I then specify the <b>Expression<\/b><i> <\/i>I want to trace. The <b>Expression<\/b> goes into a script block (delimited by a pair of curly brackets). The complete command is shown here.<\/p>\n<p style=\"padding-left: 30px\">Trace-Command -Name parameterbinding -PSHost -Expression {get-process notepad | stop-process}<\/p>\n<p>The command to trace the parameter binding of the <b>Get-Process<\/b> command as it is piped to the <b>Stop-Process<\/b> cmdlet, along with the output associated with this command is shown in the image that follows.<\/p>\n<p>&nbsp;<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2502.HSG-1-9-12-01.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2502.HSG-1-9-12-01.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<h2>Using different cmdlets in the pipeline<\/h2>\n<p>A number of cmdlets typically combine with other cmdlets to assist in working with objects on the pipeline. These cmdlets accept input via the pipeline, and then they perform services such as grouping, sorting, or filtering the information that comes through the pipeline, before they pass the input to other cmdlets. These cmdlets are:<\/p>\n<ul>\n<li>Get-Unique<\/li>\n<li>Group-Object<\/li>\n<li>Select-Object<\/li>\n<li>Sort-Object<\/li>\n<li>Tee-Object<\/li>\n<li>Where-Object<\/li>\n<\/ul>\n<p>One of the most important of these pipeline cmdlets is the <b>Where-Object <\/b>cmdlet. The reason the <b>Where-Object <\/b>cmdlet is so important is that it filters out the input it receives. It is quite common for a cmdlet to produce a lot of information. For example, the <b>Get-Process<\/b> cmdlet returns an awful lot of data, only a small portion of which appears in the default display. If I am interested in obtaining information about only processes named <b>svchost<\/b> on my computer, I could use the <b>Where-Object<\/b> cmdlet to filter out all of the process objects that did not have a name of <b>svchost<\/b>.<\/p>\n<p>In the command that follows, I first retrieve a collection of process objects by using the <b>Get-Process<\/b> cmdlet (one for each process that runs on the computer). I then send these process objects one at a time across the pipeline. When the process arrives on the other side of the pipeline, the <b>Where-Object<\/b> cmdlet examines each process.<\/p>\n<p>While the <b>Where-Object<\/b> cmdlet is looking at each process that comes across the pipeline, it uses the <b>$_ <\/b>automatic variable to represent the current object in the pipeline. Therefore, I use the <b>$_<\/b> variable to represent the current object in the pipeline, and I choose to examine the <b>Name<\/b><i> <\/i>property of that object. If the name of the object (represented by the <b>$_<\/b> automatic variable) is equal to <b>svchost<\/b>, the object passes the filter. The code that filters the <b>svchost<\/b> processes is shown here.<\/p>\n<p style=\"padding-left: 30px\">Get-Process | Where-Object { $_.name -eq &#8216;svchost&#8217;}<\/p>\n<p>There is nothing wrong with using the <b>Where-Object <\/b>to limit the return of process information to the <b>svchost<\/b><i> <\/i>process. But, in this exact example, the <b>Get-Process <\/b>cmdlet happens to have a <i>Name <\/i>parameter that will filter out processes based upon a name. The difference is that when using the <b>Where-Object <\/b>cmdlet, <b>Get-Process<\/b> returns information about every process on the system. Then all the process information enters the pipeline, and the <b>Where-Object <\/b>cmdlet performs the filtering. This is inefficient, and when working against a remote system, it can cause a significant performance hit on the command. The command to return process objects that are named <b>svchost<\/b> is shown here.<\/p>\n<p style=\"padding-left: 30px\">Get-Process -Name svchost<\/p>\n<p>If I am curious about the difference in performance between using the <b>Where-Object<\/b> or using the <i>Name <\/i>parameter, I can use the <b>Measure-Command <\/b>cmdlet to determine which command is fastest. The syntax to measure the two commands is shown here.<\/p>\n<p style=\"padding-left: 30px\">measure-command { Get-Process | Where-Object { $_.name -eq &#8216;svchost&#8217;} }<\/p>\n<p style=\"padding-left: 30px\">measure-command { Get-Process -Name svchost }<\/p>\n<p>The image that follows illustrates using the <b>Measure-command <\/b>cmdlet and the associated output from those commands.<\/p>\n<p>&nbsp;<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0830.hsg-1-9-12-2.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0830.hsg-1-9-12-2.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>On my system, the command using the <b>Where-Object <\/b>cmdlet takes 24 milliseconds, and the command that does not use the pipeline takes 1 millisecond. The <b>Measure-Command <\/b>cmdlet is not accurate with subsecond measurements, and in reality, there is no difference between 1 millisecond and 24 milliseconds. If using one command instead of the other command causes you to pause and consider the syntax, your performance gain is lost. However, if you are querying for <b>svchost<\/b><i> <\/i>information from 1000 servers that are distributed across the network, the differences are likely to become pronounced, and there could be a larger time difference between the two commands.<\/p>\n<p>DG, that is all there is to using the pipeline and filtering out data. Pipeline Week will continue tomorrow when I will talk about sorting objects.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, teaches you how to use the Windows PowerShell pipeline to increase your productivity and to avoid writing scripts. Hey, Scripting Guy! I keep hearing about the pipeline in Windows PowerShell, but I do not get it. I see commands, but I am not sure what they are actually doing. [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[51,3,4,45],"class_list":["post-11531","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-getting-started","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, teaches you how to use the Windows PowerShell pipeline to increase your productivity and to avoid writing scripts. Hey, Scripting Guy! I keep hearing about the pipeline in Windows PowerShell, but I do not get it. I see commands, but I am not sure what they are actually doing. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/11531","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=11531"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/11531\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=11531"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=11531"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=11531"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}