Find directory space on disk with powershell

3.2k Views Asked by At

I wanted to know the size of my different sub-directories, so I followed the indications of Getting Directory sizes powershell and Display directory structure in powershell and some others here in SO and somewhere else. Those functions work well, except I want to know the space that some partially synced folders occupy in disk (for example, I have a folder in Skydrive with only some of its sub-folders available online). When I call either function, they report the size of the folder as if it were in disk, not the actual disk usage. If I right-click the folder and check its properties, I can see clearly there is a difference between size and size in disk.

I have tried with Sort-Object -Property Size and Sort-Object -Property Length switches to either function (mostly based in the Get-ChildItem function), but both of them return size and not size in disk.

I guess there is a simple switch to make it take into account only actual disk usage, instead of total size, but I'm completely lost as to how to achieve it. Any leads are much appreciated!

Thanks,

2

There are 2 best solutions below

8
On BEST ANSWER

Size On Disk is the actual size that your files occupy on the drive depending on the cluster size (or allocation unit), which most of the time is 4KB, but not all of the time. It depends on the file format and how it was formatted.

As long as the files are not compressed, it's a matter of finding out how many chunks of clusters are needed to fit each file. Keeping in mind that if a file is smaller than the cluster size, it will occupy one allocation unit.

If the file is compressed, the information is not easily available and needs to be retrieved via an API.

The following code is divided in 3 main sections:

  1. It defines a Type used to access the function GetCompressedFileSizeAPI in kernel.dll. This function will retrieve the compressed size on disk of the file.
  2. It uses WMI to determine the cluster size for the given $path
  3. It computes the Size on Disk for the files in the folder and subfolders of $path depending if the file is compressed or not.

Note that the call to Get-ChildItem uses the -force switch to ensure we retrieve hidden and system files as well.

I'm not sure if this code will work with Skydrive, so you might need to change the WMI part.

$path = '.\'

Add-Type -TypeDefinition @" 
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;

public class FileInfo 
{ 
    [DllImport("kernel32.dll", SetLastError=true, EntryPoint="GetCompressedFileSize")]
    static extern uint GetCompressedFileSizeAPI(string lpFileName, out uint lpFileSizeHigh);

    public static ulong GetCompressedFileSize(string strFileName)
    {
        uint intHigh;
        uint intLow;
        intLow = GetCompressedFileSizeAPI(strFileName, out intHigh);
        int intError = Marshal.GetLastWin32Error();
        if (intHigh == 0 && intLow == 0xFFFFFFFF && intError != 0)
            throw new Win32Exception(intError);
        else
            return ((ulong)intHigh << 32) + intLow;
    }
} 
"@


$files = Get-ChildItem $path -Recurse -force | where {$_.PSIsContainer -eq $false}

$drive = [string]$files[0].PSdrive+':'
$wql = "SELECT Blocksize FROM Win32_Volume where DriveLetter='$drive'"
$driveinfo = Get-WmiObject -Query $wql -ComputerName '.' 

$sizeondisk = ($files | %{      
    if ($_.Attributes -like "*compressed*")
    {

        if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
        {
            $driveinfo.BlockSize
        }
        else
        {
            [FileInfo]::GetCompressedFileSize($_.fullname)
        }
    }
    else
    {
        if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
        {
            $driveinfo.BlockSize
        }
        else
        {
            ([math]::ceiling($_.length/$driveinfo.BlockSize))*$driveinfo.BlockSize
        }
    }
}|Measure -sum).sum

$sizeondisk

UPDATE for Sparse Files:

Let's see if this version works with sparse files, add this block of code at the end of the previous code, keep everything else the same:

$sparsesize = ($files | %{      
    if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
    {
        $driveinfo.BlockSize
    }
    else
    {
        $_.fullname
        [FileInfo]::GetCompressedFileSize($_.fullname)
    }
}|Measure -sum).sum

$sparsesize

Sources:

Understanding NTFS compression

Help querying files to return SIZE on DISK

Size of compressed files (in French)

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

Default cluster size for NTFS, FAT, and exFAT

0
On

For some reason, I was encountering errors in regards to certain values being strings and not numeric in the sparse file code snippet.

However, I noticed when I took out the " $_.fullname" from below

$sparsesize = ($files | %{      
if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
{
    $driveinfo.BlockSize
}
else
{
    $_.fullname
    [FileInfo]::GetCompressedFileSize($_.fullname)
}
}|Measure -sum).sum

$sparsesize

and change it to

$sparsesize = ($files | %{      
if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
{
    $driveinfo.BlockSize
}
else
{
    [FileInfo]::GetCompressedFileSize($_.fullname)
}
}|Measure -sum).sum

$sparsesize

Then it returned a numeric value.