Simple Where Filters

The comment about how awkward it is in PowerShell to filter out folders with Get-ChidlItem, or its alias dir, came up the other day on Twitter. I’ll be the first to admit that running a DIR command and wanting to skip folders, or perhaps you only want top level folders, is more cumbersome than we would like in PowerShell v2. In PowerrShell v3 this has been addressed but for now we’re stuck with expressions like this:

[cc lang=”PowerShell”]
dir c:\work -rec | where {!$_.PSIsContainer} | group extension | sort count

If you find yourself frequently performing this type of filtering in the shell, here’s a tip that can save a little typing. Create these two functions:

[cc lang=”PowerShell”]
function IsDir {process {$_ | Where {$_.PSIsContainer}}}
function NotDir {process {$_ | Where {!$_.PSIsContainer}}}

These are so simple I’m not even going to include a text download. You can call them whatever you want. The functions are written as filtering functions, notice the use of Process script block. In short, these are wrappers for the standard Where-Object filter we’re used to using. But now I can do this:

[cc lang=”PowerShell”]
dir c:\work -rec | notdir | group extension | sort count

Or perhaps this:

[cc lang=”PowerShell”]
dir c:\work | isdir | Select FullName,@{Name=”Size”;Expression={(dir $_.fullname -rec | notdir | measure length -sum).sum}} | format-table -auto

If you find yourself needing this every day, put the function definitions in your profile. You can use this same technique for other common filters you tend to use often. What else can you come up with?

Get File Utilization by Extension

In the past I’ve posted a few PowerShell functions that provide all types of file and folder information. The other day I had a reason to revisit one of them and I spent a little time revising and expanding. This new function, Get-Extension will search a given folder and create a custom object for each file extension showing the total number of files, the total size, the average size, the maximum size and the largest size. At it’s core, the function takes output from Get-ChildItem and pipes it to Measure-Object. But I’ve incorporated features such as filtering and the ability to run the entire function as a background job.

By default, the function searches the top level of your $ENV:Temp folder and returns a custom object for each file type.

Here’s how this works.

The function uses a few parameters from Get-ChildItem, like -Include, -Exclude and -Force. If you use one of the filtering parameters, then you also need to use -Recurse. You can specify it, or the function will automatically enable it if it detects -Include or -Exclude.

Obviously (I hope), this only works on the file system. But I went ahead and added some code to verify that the specified path is from the FileSystem provider.

Normally, I’m not a big fan of Return. But in this situation it is exactly what I want since I want to terminate the pipeline. I could have also thrown an exception here but decided not to get that wild. Assuming the path is valid, the function builds a command string based on the specified parameters.

The function will invoke this string using Invoke-Expression and filter out any folders since all I care about are files.

The results are then grouped using Group-Object. Each extension group is piped to Measure-Object to calculate the statistics based on the file’s length property.

Lastly, the function creates a custom object representing each file extension using the New-Object cmdlet.

Because I’m writing an object tot he pipeline you can further sort, filter, export or whatever. This is what makes PowerShell so flexible and valuable to IT Pros.

One thing I quickly realized, was that scanning a large folder such as Documents folder or a file share UNC, could take a long time. I could use Start-Job with my original function, but it was a bit awkward. So I decided to include -AsJob as a parameter and move the job command into the function itself. This works because I take the entire core command and wrap it in a script block.

Because of scope the scriptblock needs parameters so I can pass it my command string and the Path variable which are used within the scriptblock. After $sb has been defined, if -AsJob was specified, the function uses Start-Job to create a background job. Otherwise, it uses Invoke-Command to execute it interactively.

Use the normal job cmdlets to get the results and manage the job. But now I can run something like this:

As always I hope you’ll let me know how this works for you. The complete script has comment based help and an optional line to uncomment at the end to create an alias for the function.

Download Get-Extension.

All Hail Dir UseALot!

Some of you know my relationship with the a command prompt goes back a long, long way. Naturally I became very adept at using the DIR command, fully taking advantage of its switches to tease out hidden information or to quickly get just the information I wanted. When PowerShell first came out, I made the transition to the DIR alias without too much difficulty. Although I still found myself wanting to run a command like DIR /ad  (ie list only directories). Yes, you can achieve the same results with the PowerShell cmdlets, but that takes too much typing, especially for something I might want to use on a regular basis. I finally got around to writing a DIR function for PowerShell that better emulates my beloved DIR command from the CMD shell. Continue reading