PowerShell find non-empty UNC path folders works but is very slow

In a nutshell, I have about 30 folders called Document(s) and a number, so

Document
Documents2
...
Documents30

Below those folders, potentially, is a client folder (which can be in multiple Document(s) folders). When I'm archiving this data, I need to find non-empty folders to Robocopy over to another location.

This script will search all of the Document(s) folders for the correct client name, and then check that the folders below it aren't empty. But it's slow. Really slow. Any suggestions to speed this up?

I'm not sure if PS counts all files, or just checks that the count is > 0 and moves on. Other attempts I made to check that root folder size > 0 did not run well either; they seemed to also want to count/sum every file/folder, when I really just need to know what's > 0 bytes.

Thanks

$filePath =  "\\path\to\Document*\*"  
$folderName =  "HelloWorld"

Get-ChildItem -Force $filePath -ErrorAction SilentlyContinue | 
Where-Object { ($_.PSIsContainer -eq $true) `
-and ( $_.Name -like "*$folderName*") `
-and (Get-ChildItem -Path $_.FullName -Recurse | 
                Where-Object {$_.PSIsContainer -eq $True `
                    -and $_.GetFileSystemInfos().Count -gt 0 })  } |
Select-Object FullName | format-Table * -AutoSize 

Update

For @TheMadTechnician:

Is this correct to return the 'root' folder path for anything with non-empty subdirectories? e.g.

\\path\to\Documents3\HelloWorld
\\path\to\Documents19\HelloWorld
\\path\to\Documents20\HelloWorld

$FolderName = "HelloWorld"
$FilePath = "\\path\to\Document*\$FolderName"

$EmptyPaths = @()
ForEach($Folder in (Get-ChildItem $FilePath -Directory)){
    If((Get-ChildItem "$($Folder.FullName)\*").Count -gt 0){Continue}
    Get-ChildItem $Folder -Recurse -File|ForEach{break}
    $EmptyPaths += $Folder |
    Select-Object FullName | format-Table * -AutoSize 
} 

Upvotes: 1

Views: 582

Answers (1)

TheMadTechnician
TheMadTechnician

Reputation: 36322

I think you are going to see large speed improvements if you allow the file system provider filter the results. Then you can use a ForEach loop, check the root of each folder for files (if there's files in the root there's no need to recurse and we can continue to the next folder), and if no root files search recursively for files and break on the first file found, which will move to the next root folder:

$FolderName = "HelloWorld"
$FilePath = "\\path\to\Document*\$FolderName"
$EmptyPaths = @()
$AllRootPaths = Get-ChildItem $FilePath -Directory
ForEach($Folder in $AllRootPaths){
    If((Get-ChildItem "$($Folder.FullName)\*").Count -gt 0){Continue}
    Get-ChildItem $Folder -Recurse -File|ForEach{break}
    $EmptyPaths += $Folder
}
$AllRootPaths | Where{$_ -notin $EmptyPaths} | Select -Expand FullName

I'm betting that will speed up your results considerably. Running this in a session on the server hosting the files so you don't have to do UNC paths would be faster since it would all be local, but I realize that is often times not an option.

I use a ForEach loop, and not an in-line ForEach-Object loop because of the behavior of break, and I think we're going to get better results doing it this way.

Edit: I updated the above code to collect all root folders first, and then create the list of empty folders. Then you can output the entire list of folders, filter out the empty ones, and it should give you a listing of folders that have files in them. Try the above code and see if it works. It worked for me when I tested it locally.

Upvotes: 2

Related Questions