Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Pipeline Power

Posted on April 4, 2012

Last week I came across a blog post that had a decent example using PowerShell and PowerCLI to get the disk location for all virtual machines. The posted code works and does display the information you might be after.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!


$myVMs = get-vm

foreach($vm in $myVMs){
$myDisks = @($vm | get-harddisk)
foreach ($disk in $myDisks) {
write-host $vm.Name, ( $disk|select -ExpandProperty Filename)
}
}

But I saw an teaching opportunity. Because the code works I can't say it is "wrong", but it really doesn't adopt the PowerShell paradigm. The first issue is that using Write-Host only writes content to the console. There is no way with this command to do anything else with the results such as sorting, grouping or sending to a text file.

The other issue is the need to use ForEach. This is what we had to do in VBScript but in PowerShell we can take advantage of the pipeline.


get-vm | Select Name,@{Name="Disk";Expression= {$_ | get-harddisk | Select -ExpandProperty Filename }}

But now I can do something with this such as sorting by disk:


PS S:\> get-vm | Select Name,@{Name="Disk";Expression= {$_ | get-harddisk | Select -ExpandProperty Filename }} | sort Disk,Name

Name Disk
---- ----
Cluster Alpha {[datastore1] Cluster Alpha/Cluster ...
Globomantics Mail [datastore1] globomantics mail/Win2K...
MyCompany Exchange 2007 {[datastore1] MyCompany Exchange 200...
MyCompany XP {[datastore1] MyCompany XP/MyCompany...
MyCompany Windows 2008 [datastore1] MyCompany2008/Windows S...
MyCompanyDC 2K3R2 {[datastore1] MyCompanyDC 2K3R2/MyCo...
R2 Server Core -DEL [datastore1] Research Core DC/R2 Ser...
Cluster Bravo {[datastore2] Cluster Bravo/Cluster ...
MyCompany Vista {[datastore2] MyCompany Vista/Vista ...
...

Or if there are multiple disks, it is much easier to work with them. Write-Host can't.


PS S:\> $vminfo=get-vm | Select Name,@{Name="Disk";Expression= {$_ | get-harddisk | Select -ExpandProperty Filename }}
PS S:\> $vminfo[1].disk
[datastore2] MyCompany Vista/Vista Baseline.vmdk
[datastore2] MyCompany Vista/MyCompany Vista.vmdk
PS S:\> $vminfo | Export-Clixml c:\work\vminfo.xml

The tricky part here I realize is pulling up a value from a nested object, in this case the Filename and adding it to the VM object. I totally get that this is not something a beginner would necessarily discover on their own, which is why I write stuff like this. But the big difference is that I know have an object written to the pipeline that I can do something with and I didn't have to resort to keep track of what goes where in some foreach loops.

The other advantage, although not universal, is performance. Running the ForEach code against my 23 VMs took almost 6 seconds. My PowerShell one line took a tad over 3 seconds.

I don't want you to think you can never use Write-Host or ForEach. Sometimes they make sense and may even perform better. But always ask yourself if you are thinking the PowerShell way and pushing objects through the pipeline or are you writing something that could be mistaken for VBScript.

By the way, I have posted most of this on the blog as a comment that is awaiting moderation. But I figured I would share it with my readers as well.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

4 thoughts on “Pipeline Power”

  1. Jon says:
    April 4, 2012 at 5:10 pm

    You lost me when you criticized the use of ForEach. Yes PowerShell has the pipeline. It’s also often harder to read and harder to write for people who aren’t PowerShell gurus. Personally, if I’m writing a script that I intend to run multiple times, I only use the pipeline when it makes sense. I make frequent use of ForEach loops, instead of the pipeline, because it makes the script more readable to me, and is more readable to other programmers who may not know PowerShell as well as I do.

    1. Jeffery Hicks says:
      April 4, 2012 at 6:06 pm

      Thank you for your comment. Perhaps I was a bit harsh on the use of ForEach. It definitely has its place and I use it when the right situation arises. In this particular case, based on the original intent, I think a better choice could have been made. Plus, Write-Host doesn’t bring anything to the party in this situation. Again, Write-Host has its place, but I want to use PowerShell commands that leverage the pipeline and cmdlet where it makes sense. And I am not a programmer nor are the people who read my books, sit in on conference sessions or attend my classes: I am an IT Pro and approach PowerShell from that perspective. The more efficient you can be at the command prompt in getting the information you want the better. If it takes a few commands that’s fine. Creating a script simply taking commands that work in the shell and tossing them in a script file so you only have to type them once. It is not uncommon for beginners to think of PowerShell like VBScript and that is a hard paradigm to shake. But you have to at least be able to recognize it.

  2. walid toumi says:
    April 5, 2012 at 2:05 am

    my tests:

    PS ~walid2mi>
    (measure-command {
    dir . | ?{$_.PsIsContainer -eq $true} |
    foreach {
    # For each folder look through all the files (not folders) and display the most recent file.
    dir $_.fullname -recurse | ?{$_.PsIsContainer -ne $true}| sort -prop LastWriteTime |select -last 1}
    }).Milliseconds

    >>> 131
    PS ~walid2mi>
    (measure-command {
    foreach($item in (dir . | ? {$_.PSIsContainer})) {
    dir $item.fullname -rec | ?{!$_.PsIsContainer}| sort LastWriteTime | select -last 1}
    }).Milliseconds

    >>> 126
    PS ~walid2mi>

    1. Jeffery Hicks says:
      April 5, 2012 at 7:55 am

      I never intended to convey the impression to never use ForEach. In some cases it makes sense and can even perform better as it does in your example. Although I have to say your example shows PowerShell thinking as you are still using some implicit pipelining. And logically, given what you are trying to do your second example makes more sense and is easier to follow. Here’s another post on this topic https://jdhitsolutions.com/blog/2011/11/foreach-or-foreach-object/.

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d