Home > Software design >  Powershell slowness in "get-childitem . -directory -recurse" when there are lots of files
Powershell slowness in "get-childitem . -directory -recurse" when there are lots of files

Time:05-11

I run:

PS F:\> gci F:\logs\PRV_RequestLogs\inbound -r -directory | %{ $_.fullname }

and it shows:

F:\logs\PRV_RequestLogs\inbound\2020-02-03
F:\logs\PRV_RequestLogs\inbound\2020-02-04
...
F:\logs\PRV_RequestLogs\inbound\2022-05-09
F:\logs\PRV_RequestLogs\inbound\2022-05-10

then it "hangs" there.

Then I run the following command in another windows trying to find what is going on:

PS F:\> C:\temp\handle64.exe -p 3204

and I found:

  ACC: File  (RWD)   F:\logs\PRV_RequestLogs\inbound\2020-04-28
  ...
  F08: File  (RWD)   F:\logs\PRV_RequestLogs\inbound\2020-04-28

and the directory keep changing, so So it traverse each directory tring to find sub-directories, there are none but lots of files.

It took hours to complete the process. Never thought of the process would be so slow. It looks like it going through each file and test if it is a directory. Is there a quicker way to do this? I am using powershell 5.0 on Windows 2012R2.

CodePudding user response:

Get-ChildItem is known to be slow to traverse directories recursively but it is what is built-in to PowerShell, it's a very handy and easy to use cmdlet. If you're looking for speed and efficiency you may need to default to .NET API calls to IO.Directory.

Haven't tested this enough but I believe it should be working, as in, finding any directory that is empty and is older than 90 days in this case.

Worth noting that this code requires .NET Framework 4 if running Windows PowerShell.

$queue = [Collections.Generic.Queue[IO.DirectoryInfo]]::new()
$olderThan = [datetime]::Now.AddDays(-90)         # => Set limit date here!
$queue.Enqueue('F:\logs\PRV_RequestLogs\inbound') # => Starting path here!

while($queue.Count) {
    $dir = $queue.Dequeue()
    try {
        $isEmpty = $true
        foreach ($i in $dir.EnumerateDirectories()) {
            if($i.Attributes -band [IO.FileAttributes]::ReparsePoint) {
                # skip if it is ReparsePoint
                continue
            }
            $isEmpty = $false
            $queue.Enqueue($i)
        }

        if($isEmpty -and $dir.CreationTime -lt $olderThan -and -not $dir.EnumerateFiles().GetEnumerator().MoveNext()) {
            # output the directory if the 3 conditions are `$true`, no folders or files and older than
            $dir
        }
    }
    catch {
        # if we were unable to enumerate this directory, go next
        continue
    }
}

Relevant Documentation

  • Related