The Surly Admin

Father, husband, IT Pro, cancer survivor

Get File Sizes by Extension

As I’ve mentioned before, I enjoy trolling the scripts at Spiceworks and converting the vbScripts that are interesting over to Powershell in an attempt to see if I can not only shorten them, but improve them.  One interesting example came by last week, the Get File Size for a Specific Extension script.  Here’s what I did with it.

One Liner

With Powershell, we can actually convert this 65 line script into a one liner:

"{0:N2}MB" -f ((gci -path c:\users\martin\dropbox -recurse -include *.jpg | Measure-Object length -Sum).Sum
/ 1mb)

Output:
157.80MB

But the output is pretty uninspired.  Also, it’s not really matching the output created by the original script.  Here’s what it comes up with on the same directory path.


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Also, the vbScript has a nice file path common dialog box for selecting the file path.  That means we have to go beyond a simple Read-Host command to match this script.  Also, let’s be honest, vbScript’s ability to go through a folder directory is completely non-existent, you have to code everything.  Obviously this gives any scripting language with folder commands (like Powershell) a gigantic leg up, so going from the 65 lines needed in vbScript to something smaller should be pretty easy, and it was.  So let’s go deeper and create this Powershell script.

Param (
[string]$Path = ($((New-Object -ComObject "Shell.Application").BrowseForFolder(0,"Select a folder:",0)).Self.Path),
[string]$Extension = (Read-Host "Please enter the file-extension")
)

As always, I like to define my user variables as parameters which allows the script to be changed on the fly without touching code.  This also matches the functionality presented in our original script.  The interesting piece here is the $Path variable.  Instead of using a Read-Host cmdlet and asking the user to provide the path themselves, we want to present a Windows Common Dialog Box.  I did this by creating a ComObject in Shell.Application and invoking the BrowseForFolder method and that object was then assigned to $Path.

Now we need to get the actual path out of our $Path variable.  Right now $Path is a ComObject, here’s some information about it.

Type and Get-Member information

The first thing I noticed in the vbScript was the author invoking the Self property, and then the Path property. A quick look at the Get-Member information on $Path revealed why this was necessary.  There is not path property on the ComObject itself, though there is a lot of interesting information in there that we’ll have to store away for another day!  Next we want to invoke the .Self property and see what information we get.

Lots of great information in here, but specifically we have a Path property, and another nice feature of Powershell is the ability to “dot source” things which means instead of several lines working down the properties, we can do it all in one line:  …).Self.Path.  Why do it all in the Param, you might ask?  Well, there are 2 reasons really.  One that makes sense, and one…

First, it’s just cool to make that one gigantic line, isn’t it?!

Second, since this is a Param we can’t really make it an object since if you decide to use the -Path option when invoking the script how the heck do you type an object?  I don’t know, so we need the end result to be a string–notice we specify $Path is a string by putting [string] in front of the variable?  So by the time the value comes into $Path we need it to be a string value to support manual entry.

Last we use a simple Read-Host cmdlet to get the extension information.

Now we have our path and our extension from the user let’s get to work!

$Files = Get-ChildItem $Path -Include *.$Extension -Recurse
If ($Files.Length)
{ Write-Host "$(@($Files).Count) File(s) found."
Write-Host "Total Size of *.$Extension files: $('{0:N2}' -f (($Files | Measure-Object length -Sum).Sum / 1mb))MB`n`n"
}
Else
{ Write-Host "No files exist with the extension *.$Extension"
}

This is the rest of the script.  The Get-ChildItem cmdlet is pretty standard, we just get all of the files from the path, and use the -Include parameter to limit the output to our designated extension.  Use the -Recurse parameter to pull all of the sub-directories as well.  Two things of interest here are in the 2 Write-Host lines, starting with the use of the .Count property.

Write-Host "$(@($Files).Count) File(s) found."

The File_System object that we create with Get-ChildItem does not have a .Count property, but it does have .Length property and this property does reflect the number of files collected with Get-ChildItem.  So why not use Length?  The problem occurs when you only have 1 file in the collection.  Powershell will then use the length of the path for the .Length property instead of the count.  So you’d end up with a file count of several hundred instead of 1.  Instead, I force the variable into an array by surrounding it with @() and then use the .Count property available in arrays to get the correct number.  Then we need to surround the entire thing in $() so that Write-Host won’t actually show “.Count” as text, but will recognize that we want the .Count property.

The second line is simply an adaptation of our one-liner at the beginning to get the actual file sizes and present them in a pleasing manner.

And that’s it, we’ve successfully replicated all of the capabilities of the vbScript–all 65 lines of it–into 14 lines of Powershell code.  As is my usual procedure, I don’t want to be rude and step all over the original author poster by putting the source code on Spiceworks so I’ll post the whole thing here.

Param (
[string]$Path = ($((New-Object -ComObject "Shell.Application").BrowseForFolder(0,"Select a folder:",0)).Self.Path),
[string]$Extension = (Read-Host "Please enter the file-extension")
)
cls
Write-Host "`nSearching $Path for *.$Extension...`n"
$Files = Get-ChildItem $Path -Include *.$Extension -Recurse
If ($Files.Length)
{ Write-Host "$(@($Files).Count) File(s) found."
Write-Host "Total Size of *.$Extension files: $('{0:N2}' -f (($Files | Measure-Object length -Sum).Sum / 1mb))MB`n`n"
}
Else
{ Write-Host "No files exist with the extension *.$Extension"
}
Advertisements

November 10, 2012 - Posted by | PowerShell | , ,

3 Comments »

  1. […] from this script, I decided to expand on it and have Powershell get all of the extensions and report on […]

    Pingback by Get File Counts and Size for All Extensions « The Surly Admin | November 26, 2012 | Reply

  2. Great script, thanks!
    If you change the line above to this you will get an output without duplicates:
    { $Extensions = $AllFiles | Select Extension | Sort Extension -Unique

    Comment by Dave | September 13, 2013 | Reply

    • Hi Dave, yes I’ve gotten a couple of requests on that and I believe I’ve actually added a parameter to the source code where you can specify if you want detailed information or not. Just haven’t updated this post!

      Comment by Martin9700 | September 13, 2013 | Reply


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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: