powershellrecursiondepthdirectory-structure

List folders at or below a given depth in Powershell


I have a directory which contains a lot of folders. I want to list all folder (path) that go deeper than 2 levels. So in below case folder 1 & 2.

Directory/folder1
Directory/folder1/test1/test/testsub
Directory/folder1/test2
Directory/folder1/test3
Directory/folder2/blablabla/bla/1
Directory/folder3/test
Directory/folder4/test
Directory/folder5/test

I was trying the following:

$Depth = 3
$Path = "."

$Levels = "\*" * $Depth
$Folder = Get-Item $Path
$FolderFullName = $Folder.FullName
Resolve-Path $FolderFullName$Levels | Get-Item | ? {$_.PsIsContainer} | Write-Host

Solution

  • The solution immediately below, which builds on your own, assumes that your intent is to find those child directories whose subtrees exceed a given depth.

    If you instead want to find all directory paths that are at a given depth or deeper, see the bottom section.
    Your approach cannot achieve that, because it finds directories at the given depth only, not also below.


    Your own clever wildcard-based approach should work in principle, but:

    (a) Streamlining your approach:

    $Depth = 3
    $Path = '.'
    
    $Levels = '/*' * $Depth
    Get-ChildItem -Directory $Path/$Levels
    

    (b) Limiting output to the distinct set of top-level folder with too-deep subtrees:

    $Depth = 3
    $Path = '.'
    
    $Levels = '/*' * $Depth
    Get-ChildItem -Directory $Path/$Levels |
      ForEach-Object { ($_.FullName -split '[\\/]')[-$Depth] } |
        Select-Object -Unique
    

    Note: Splitting by [/\\] - that is, by either / or \ - makes the solution work on Unix-like platforms too (PowerShell Core); on Windows, -split '\\' (by an escaped \) is sufficient.

    With your sample folder hierarchy, the above would yield:

    folder1
    folder2
    

    Optional reading: Getting folders up to, at, or beyond a certain depth:

    Note:


    At-a-given-depth-only logic:

    This is the same logic employed in the solution above; the following code lists folders at depth 2 only, i.e. those at the grandchild level (child directories of child directories) - note that, unlike with Get-ChildItem -Depth, depth counting starts with 1, i.e. 1 refers to child directories:

    $depth = 2 # grandchild directories only
    
    Get-ChildItem -Directory -Path ('*/' * $depth)
    
    $depth = 2 # grandchild directories
    
    # Calculate the length of the path prefix for determining relative paths.
    # (Using the current dir ($PWD) as the reference path here.)
    $PrefixLen = (Convert-Path -LiteralPath $PWD).Length + 1
    
    $Levels = '/*' * $Depth
    Get-ChildItem -Directory -Path ('*/' * $depth) |
      ForEach-Object { $_.FullName.Substring($PrefixLen) }
    

    Up-to-a-given-depth logic:

    The PSv5+ -Depth parameter limits Get-ChildItem's recursion depth, i.e., it only finds items up to the specified depth, but note that it is depth 0, not 1 that represents the immediate children.
    Note that use of -Depth implies -Recurse, though you may specify the latter as well.

    For instance, to enumerate child folders and grandchild folders (2 levels) in the current directory, use:

    $depth = 2 # child and grandchild directories
    
    Get-ChildItem -Directory -Depth ($depth - 1)
    

    At-a-given-depth-or-deeper logic:

    Limiting results to items at levels greater than or equal to a given depth requires a custom solution:

    $depth = 2 # grandchild directories and below
    
    Get-ChildItem -Directory -Name -Recurse |
      Where-Object { ($_ -split '[/\\]').Count -ge 2 } |
        Get-Item -LiteralPath { "$PWD/$_" }
    

    If your input path isn't the (implied) current dir., substitute that path for $PWD.