Quick tip: new syntax revisited

You probably seen this a lot: new Foreach-Object and Where-Object syntax without script block is something people blogged about a lot. Some people love it, some hate it, some see it as a source of confusion for new-comers. I personally love it and I try to use it whenever possible (but only when working in interactive shell). You will see most of time new syntax works great, but it fails badly when used in more complex filters. I would like to show you how it can be used in combination with old syntax to simplify your code even in more complex situations.

I had pretty annoying issue at work this week: on few servers C drive was getting full very quickly, and I couldn’t find root cause. I decided to generate drive usage report “before” and “after” to see folders that have biggest delta and eventually, identify what is causing drive consumption. I had pretty simple script “handy” that was providing me with data and any point in time, so it was no-brainer to create “snapshots”.

It was a moment when I wanted to generate “delta” report when I started to play with idea of using new syntax rather than “tricks” I’ve used in the past.

Tricks are necessary whenever you have a need to use nested where/ foreach. Because within nested script blocks you “loose” connection to top-level $_ – you have to store in some variable first:

$Before = Import-Csv -Path C:\temp\Disk-E-Before.csv            
Import-Csv -Path C:\temp\Disk-E-After.csv |             
    ForEach-Object {            
        $Current = $_            
        ($Before |             
            Where-Object { $_.Path -EQ $Current.Path } |             
            ForEach-Object { $_.Size }            
        ) - $Current.Size            
        # ... or ...            
        foreach ($Item in $Before) {            
            if ($Item.Path -eq $_.Path) {            
                $Item.Size - $_.Size            

Well, I was using Select-Object and calculated properties, but you should get the point. In v4 I would use pipeline variable common parameter to walk around it, but I was running my code on server with v3 on it. So how we can benefit from new syntax? We have relatively simple (though nested) filter: give me one item from $Before that has same ‘Path’ property as my current pipeline item. Simple syntax makes it possible without additional variable. My final code looked bit like this one (this time with Select-Object, that I actually used):

$Before = Import-Csv -Path C:\temp\Disk-E-Before.csv            
Import-Csv -Path C:\temp\Disk-E-After.csv |             
    Select-Object Path, DaysFromLastChange, Size, @{            
        Name = 'Delta'            
        Expression = {            
            ($Before |             
                Where-Object Path -EQ $_.Path |             
                ForEach-Object Size            
            ) - $_.Size            

Using new syntax in Where-Object I can compare value of property of current pipeline element from nested pipe (I get it using positional parameter –Property) to value of any property of item from “top level” pipeline (represented by $_ in Select-Object script blocks). Because I was working in interactive console I was not concerned with any confusion it could cause for potential reader of my code. That gave me pretty short one-liner, without extra semicolons:

ipcsv C:\temp\Disk-E-After.csv | select Path, {($Before | ? Path -EQ $_.Path).Size - $_.Size}            

I know it’s cryptic (that’s why you have full code above) but when I’m in console, quick & dirty is good enough: I want results, not nice formatting. And with simplified syntax I could get to results quickly, even though I needed to compare items on different “pipe” levels. Something that –Pipeline variable addresses in v4:

ipcsv C:\temp\Disk-E-After.csv -pv i | select Path, {($Before | ? { $_.Path -eq $i.Path }).Size - $i.Size}

So: as you can see new syntax can be used in more complex filters too, you just have to know its limitations. And remember, that when you use it like that, $_ is not the same thing as it would be if you would use old syntax.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s