Private/Get-DirectoryThreaded.ps1
|
#!/usr/bin/env pwsh Set-StrictMode -Version Latest function Get-DirectoryThreaded { <# .SYNOPSIS Recursively returns the directories under a directory, on one thread or in parallel. .DESCRIPTION Recurses -Directory with .NET enumeration and a stack, and returns every directory below it. -Directory itself is not returned. With -Parallel, returns each directory directly in -Directory, then recurses into each of them on its own worker, -ThrottleLimit directories at a time, using Invoke-ParallelJob. Starting workers costs time, so use -Parallel for large trees, such as /, and leave it off for small ones. Directories whose name is in -Exclude are skipped entirely: they are not returned and their contents are never read. Directories that cannot be read are skipped instead of stopping the recursion. Symbolic links and junctions are not followed. Hidden and system directories are included. With -Parallel, directories are returned as workers find them, not in tree order. .PARAMETER Directory The directory to recurse. Accepts directories from the pipeline. .PARAMETER Exclude Directory names to skip, such as proc or .git. Matched against the directory name only, not its path. Defaults to none. .PARAMETER Parallel Recurses the subdirectories of -Directory in parallel thread jobs. .PARAMETER ThrottleLimit With -Parallel, the number of subdirectories recursed at once, from 1 through 255. Defaults to 75% of the processors, rounded down, and at least 1. .INPUTS System.IO.DirectoryInfo. You can pipe directories to this function. .OUTPUTS System.IO.DirectoryInfo. One object for each directory. .EXAMPLE Get-DirectoryThreaded -Directory $HOME/Develop/PSSecurity -Exclude '.git' Returns the directories in the repository on one thread, skipping .git. .EXAMPLE Get-DirectoryThreaded -Directory / -Exclude 'proc', 'sys', 'dev' -Parallel Returns every directory on the system in parallel, skipping the proc, sys, and dev pseudo filesystems. .EXAMPLE Get-Item -LiteralPath $HOME/Develop | Get-DirectoryThreaded -Exclude 'node_modules' -Parallel -ThrottleLimit 2 Returns the directories under ~/Develop, two subdirectories at a time. #> [CmdletBinding()] [OutputType([System.IO.DirectoryInfo])] param( [Parameter(Mandatory, ValueFromPipeline, HelpMessage = 'Enter the directory to recurse. Accepts directories from the pipeline.')] [System.IO.DirectoryInfo]$Directory, [Parameter(HelpMessage = 'Enter the directory names to skip, such as proc.')] [string[]]$Exclude = @(), [Parameter(HelpMessage = 'Recurse the subdirectories in parallel thread jobs.')] [switch]$Parallel, [Parameter(HelpMessage = 'Enter the maximum number of workers (1-255).')] [ValidateRange(1, 255)] [int]$ThrottleLimit ) begin { if (-not $PSBoundParameters.ContainsKey('ThrottleLimit')) { $ThrottleLimit = Get-DefaultThrottleLimit } # Setting AttributesToSkip replaces the default of Hidden and System, so only # links are skipped. IgnoreInaccessible skips unreadable directories. $enumerationOptions = [System.IO.EnumerationOptions]@{ IgnoreInaccessible = $true AttributesToSkip = [System.IO.FileAttributes]::ReparsePoint } # Runs here on one thread, and inside workers with -Parallel, so it must not # call this module's functions. $recurseDirectory = { param($Exclude, $EnumerationOptions) begin { $pending = [System.Collections.Generic.Stack[System.IO.DirectoryInfo]]::new() } process { $pending.Push($_) while ($pending.Count -gt 0) { foreach ($subDirectory in $pending.Pop().EnumerateDirectories('*', $EnumerationOptions)) { if ($subDirectory.Name -notin $Exclude) { $subDirectory $pending.Push($subDirectory) } } } } } } process { if ($Parallel) { $topDirectories = @( $Directory.EnumerateDirectories('*', $enumerationOptions) | Where-Object -Property Name -NotIn $Exclude ) foreach ($topDirectory in $topDirectories) { $topDirectory } $parallelParams = @{ ThrottleLimit = $ThrottleLimit ArgumentList = $Exclude, $enumerationOptions ScriptBlock = $recurseDirectory } $topDirectories | Invoke-ParallelJob @parallelParams } else { $Directory | & $recurseDirectory $Exclude $enumerationOptions } } } |