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.
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" }
[…] from this script, I decided to expand on it and have Powershell get all of the extensions and report on […]
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
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!
The one liner only provides size of the folders which are not hidden.How can we include Hidden folders in that line?