Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the actual size-on-disk of a file from PowerShell?

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.)

like image 226
JMarsch Avatar asked Feb 16 '09 18:02

JMarsch


1 Answers

(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.

like image 65
JMarsch Avatar answered Sep 22 '22 06:09

JMarsch