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,
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:
GetCompressedFileSizeAPI
in kernel.dll. This function will retrieve the compressed size on disk of the file.$path
$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?