March PowerShell Oneliner

In this month’s SAPIEN newsletter I offered a PowerShell oneliner to returns usage information for a specified folder. I don’t have room in the newsletter to go into detail so here is more in-depth coverage of this oneliner. I’ll also have a downlink link at the end.

read-host "Enter a folder path" -ov fldr | dir -recurse -force `
-ea "SilentlyContinue" | measure-object length -sum -max -average |
Select-Object @{name="Folder";Expression={$fldr}},`
@{name="Total Files";Expression={$_.count}},`
@{name="Largest File(MB)";Expression={"{0:F2}" -f ($_.maximum/1MB)}},`
@{name="Average Size(MB)";Expression={"{0:F2}" -f ($_.average/1MB)}},`
@{name="Total Size(MB)";Expression={"{0:F2}" -f ($_.sum/1MB)}}

The oneliner starts out using Read-Host which prompts you for a directory path. Enter a path with out any quotes, even if it has spaces in the name. You’ll also notice I’m using the common parameter -OutVariable, which has an alias of ov. I need to save the path name for later.

The path name is piped to Get-Childitem, I’m using the DIR alias. I’m doing a recursive operation using the -force parameter to include hidden files and folders. You’ll also notice I’m using another common parameter -ErrorAction which has a convenient alias of ea. This parameter instructs the cmdlet on what to do when an error occurs. In my case, I don’t want to see any error messages or stop.  The SilentlyContinue value instructs the cmdlet to keep going, essentially ignoring any errors. On Vista there are a number of folders that are reparse points that will raise errors when accessed from the shell and I don’t care to see these errors.

The output is then piped to the Measure-Object cmdlet. I want to return some statistics based on the file size, or length property. I instruct the cmdlet to return the sum, maximum value and average value based on the length property.

Remember, at this point, the file objects are no longer in the pipeline. Instead Measure-Object is sending out a measurement object which you can verify by piping  Measure-Object expression to Get-Member. I use Select-Object and create custom properties using hash tables. The first property is for the folder name. The value is pulled from the variable I used for -OutVariable at the beginning of the expression. The “Total Files” property is the Count value from the measurement object. The largest file value is the maximum property formatted to display as MB. I do the same thing for average and sum.

Running the expression will result in output like this:

Folder           : c:\users\jeff\documents
Total Files      : 2244
Largest File(MB) : 18.75
Average Size(MB) : 0.23
Total Size(MB)   : 521.78

I could have used one of the Format cmdlets to create the custom object, but I like using Select-Object as it offers more flexibility. I can export to a CSV file, create an HTML report or create a formatted table with autosized columns.

I would even take this a step further and save this entire one liner as a script block.

$usage={read-host "Enter a folder path" -ov fldr | 
dir -recurse -force -ea "SilentlyContinue" |
measure-object length -sum -max -average |
Select-Object @{name="Folder";Expression={$fldr}},`
@{name="Total Files";Expression={$_.count}},`
@{name="Largest File(MB)";Expression={"{0:F2}" -f ($_.maximum/1MB)}},`
@{name="Average Size(MB)";Expression={"{0:F2}" -f ($_.average/1MB)}},`
@{name="Total Size(MB)";Expression={"{0:F2}" -f ($_.sum/1MB)}}
}

Now all I have to do is invoke the script block.

PS C:\> &$usage

The oneliner should work for any directory path you can use with Get-ChildItem.

Download a text file with this oneliner here.