powershellmicrosoft-distributed-file-systemlong-path

Running command to determine top 32 largest files returns filename length too large error


Following this guide. It says to "enable maximum path length support." I've done that but I still get these errors:

gci : The specified path, file name, or both are too long. The fully qualified file name must be less than 260
characters, and the directory name must be less than 248 characters.
At line:1 char:69
 ... l = New-Object System.Collections.ArrayList; gci D:\ -file -r | ForEa ...
                                                  ~~~~~~~~~~~~~~~~
    + CategoryInfo          : ReadError: (D:\PathChanged\Etc...RMATION FY 2012:String) [Get-ChildItem], PathTooLongExcept
   ion
    + FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand

I'm running a script from A better way of calculating top-32 file sizes for DFS folder staging size specfically:

measure-command { $total = New-Object System.Collections.ArrayList; 
gci F:\Folder\with\966693\items -file -r | 
ForEach { $total.Add($_.length)>$null } ; 
(($total | sort -descending | select -first 32 |measure-object -sum).sum/1GB) }

I've run the following command to enable maximum file length support:

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

Solution

  • You can use the \\?\ prefix to overcome the PathTooLongException. See Maximum Path Length Limitation for details on this.

    As for how you could improve your code, a very efficient way to get the sum of the 32 largest files is with the help of LINQ's OrderByDescending, Take and Sum using EnumerateFiles as your IEnumerable source. This method requires EnumerationOptions so it's only available in PowerShell 7+.

    $dir = [System.IO.DirectoryInfo]::new('\\?\D:\')
    $options = [System.IO.EnumerationOptions]@{
        RecurseSubdirectories = $true
        IgnoreInaccessible    = $true
        AttributesToSkip      = [System.IO.FileAttributes]::ReparsePoint
    }
    
    $selector = [System.Func[System.IO.FileInfo, long]] { $args[0].Length }
    
    [System.Linq.Enumerable]::Sum(
        [System.Linq.Enumerable]::Take(
            [System.Linq.Enumerable]::OrderByDescending(
                $dir.EnumerateFiles('*', $options),
                $selector),
            32),
        $selector)
    

    In Windows PowerShell 5.1 the easiest way is to use Get-ChildItem with Sort-Object, Select-Object and Measure-Object however this is slower:

    Get-ChildItem -LiteralPath '\\?\D:\' -Recurse -File |
        Sort-Object -Descending Length |
        Select-Object -First 32 |
        Measure-Object Length -Sum