I am writing an administrative script, and I need to calculate the size of files on disk.
These files are on a compressed NTFS volume.
I can't use FileInfo.Length
, because that is file size and not size on disk. For example, if I have a 100MB file, but it is only using 25MB due to NTFS compression, I need my script to return 25MB.
Is there a way to do this in Powershell?
(I know about the GetCompressedFileSize()
Win32 call, but I was hoping that this is already wrappered at some level.)
(edit)
I figured out how to dynamically add a property (called a "script property") to the Fileobject, so now, I can use the syntax: $theFileObject.CompressedSize to read the size.
(end of edit)
Read Goyuix's response, and I thought "Cool, but isn't there some kind of type-extension capability in Powershell?". So then I found this Scott Hanselman post: http://www.hanselman.com/blog/MakingJunctionsReparsePointsVisibleInPowerShell.aspx
And I created a Script Property for the FileInfo object: CompressedSize.
Here's what I did: (note: I'm quite new to Powershell, or at least I don't use it much. This could probably be made a lot better, but here's what I did:
First, I compiled the Ntfs.ExtendedFileInfo from Goyuix's post. I put the DLL in my Powershell profile directory (Documents\WindowsPowershell)
Next, I created a file in my profile directory named My.Types.ps1xml.
I put the following XML into the file:
<Types>
<Type>
<Name>System.IO.FileInfo</Name>
<Members>
<ScriptProperty>
<Name>CompressedSize</Name>
<GetScriptBlock>
[Ntfs.ExtendedFileInfo]::GetCompressedFileSize($this.FullName)
</GetScriptBlock>
</ScriptProperty>
</Members>
</Type>
</Types>
That code (once merged into the type system) will dynamically add a property named CompressedSize to the FileInfo objects that are returned by get-childitem/dir. But Powershell doesn't know about the code yet, and it doesn't know about my DLL yet. We handle that in the next step:
Edit Profile.ps1. in the same directory. Now, my Profile file happens to already have some stuff in it because I have the Community Extensions for powershell installed. Hopefully, I'm including everything that you need in this next code snippet, so that it will work even on a machine that does not have the extensions. Add the following code to Profile.ps1:
#This will load the ExtendedfileInfo assembly to enable the GetCompressedFileSize method. this method is used by the
#PSCompressedSize Script Property attached to the FileInfo object.
$null = [System.Reflection.Assembly]::LoadFile("$ProfileDir\ntfs.extendedfileinfo.dll")
#merge in my extended types
$profileTypes = $ProfileDir | join-path -childpath "My.Types.ps1xml"
Update-TypeData $profileTypes
Now, the $ProfileDir variable that I reference is defined earlier in my Profile.ps1 script. Just in case it's not in yours, here is the definition:
$ProfileDir = split-path $MyInvocation.MyCommand.Path -Parent
That's it. The next time that you run Powershell, you can access the CompressedSize property on the FileInfo object just as though it is any other property. Example:
$myFile = dir c:\temp\myfile.txt
$myFile.CompressedSize
This works (on my machine, anyway), but I would love to hear whether it fits in with best practices. One thing I know I'm doing wrong: in the Profile.ps1 file, I return the results of LoadFile into a variable that I'm not going to use ($null = blah blah). I did that to suppress the display of result of load file to the console. There is probably a better way to do it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With