Skip to content

Instantly share code, notes, and snippets.

@sean-m
Last active May 19, 2017 21:16
Show Gist options
  • Save sean-m/a7c8dcf3e98bdd287c7f76c77de03518 to your computer and use it in GitHub Desktop.
Save sean-m/a7c8dcf3e98bdd287c7f76c77de03518 to your computer and use it in GitHub Desktop.
function Get-FileSize {
<#
.Synopsis
Gets file info, recursively if desired.
.DESCRIPTION
Enumerates files in a directory getting both the regular
and compressed size. The enumeration happens in a pipeline
function so it's memory efficient. The input parameter $Path
is checked for type so if you pipe the output of Get-ChildItem
into the function it will use the FullName parameter as the path.
Sean McArdle, Nov 2017
.EXAMPLE
Get-FileSize $env:temp | select FullName, Length, SizeOnDisk | FT -AutoSize
#>
[CmdletBinding()]
[OutputType([PSObject])]
param (
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$Path,
[Parameter(Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=1)]
[switch]$Recurse,
[switch]$NoCompressCheck
)
begin {
$ntype =@"
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public class NativeHelper_File {
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint GetCompressedFileSizeW([In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName, [Out, MarshalAs(UnmanagedType.U4)] out uint lpFileSizeHigh);
public static UInt64 GetCompressedFileSize(string FilePath, bool ErrAsZero=false) {
uint highSize;
uint lowSize = GetCompressedFileSizeW(FilePath, out highSize);
int err = Marshal.GetLastWin32Error();
if (highSize == 0 && lowSize == 0xFFFFFFFF && err != 0) {
if (ErrAsZero) return 0;
throw new System.ComponentModel.Win32Exception(err);
}
return ((ulong)highSize << 32 | lowSize);
}
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
private static extern bool PathCompactPathEx([Out] StringBuilder pszOut, string szPath, int cchMax, int dwFlags);
public static string PathCompactPath(string Path) {
StringBuilder sb = new StringBuilder(247);
PathCompactPathEx(sb, Path, 246, 0);
return sb.ToString();
}
public static IEnumerable<string> GetFiles(string root, string searchPattern, bool Recurse = false) {
var pending = new Stack<string>();
pending.Push(root);
while (pending.Count != 0) {
var path = pending.Pop();
string[] next = null;
try {
next = Directory.GetFiles(path, searchPattern);
}
catch { }
if (next != null && next.Length != 0)
foreach (var file in next) yield return file;
try {
if (Recurse) {
next = Directory.GetDirectories(path);
foreach (var subdir in next) pending.Push(subdir);
}
}
catch { }
}
}
}
"@
if(-not ([System.Management.Automation.PSTypeName]'NativeHelper_File').Type) {
Add-Type -TypeDefinition $ntype -Language CSharp -IgnoreWarnings
}
}
process {
## Check if taking in FileInfo objects,
$unwrap = $false
$unwrap = [bool]($Path.GetType() -like [System.IO.DirectoryInfo])
$filter = "*.*"
$p = [String]::Empty
if ($unwrap) {
$p = $Path.FullName
}
else {
$p = $Path
}
foreach ($fname in [NativeHelper_File]::GetFiles($p, $filter, $Recurse)) {
[UInt64]$size = 0
[UInt64]$csize = 0
## Handle long path name
if ($fname.Length -gt 247) {
$fname = [NativeHelper_File]::PathCompactPath($fname)
}
## Get FileInfo
$f = New-Object System.IO.FileInfo $fname
## File size on disk
if ((-not $NoCompressCheck) -and `
($f.Attributes.HasFlag([System.IO.FileAttributes]::Compressed))) {
$csize = $csize + [NativeHelper_File]::GetCompressedFileSize($fname, $true)
}
else {
$csize = $f.Length
}
$f | Add-Member -MemberType NoteProperty -Name SizeOnDisk -Value $csize -Passthru
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment