Question:
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.
1 2 3 4 5 6 7 8 9 |
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:
1 2 3 4 5 6 7 8 |
$Depth = 3 $Path = "." $Levels = "\*" * $Depth $Folder = Get-Item $Path $FolderFullName = $Folder.FullName Resolve-Path $FolderFullName$Levels | Get-Item | ? {$_.PsIsContainer} | Write-Host |
Answer:
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) it can be greatly streamlined.
- (b) additional work is needed to limit output to the distinct list of those child folders whose subtrees are too deep.
(a) Streamlining your approach:
1 2 3 4 5 6 |
$Depth = 3 $Path = '.' $Levels = '/*' * $Depth Get-ChildItem -Directory $Path/$Levels |
- As in your own approach,
'/*' * $Depth
dynamically creates a multi-directory-level wildcard expression (e.g.,/*/*
for a$Depth
of2
) that can be appended to the input$Path
to match only paths at that level. - The
-Directory
switch (PSv3+) limits matching to directories only.
(b) Limiting output to the distinct set of top-level folder with too-deep subtrees:
1 2 3 4 5 6 7 8 |
$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:
1 2 3 |
folder1 folder2 |
- If you want the full paths instead, append
| Convert-Path -LiteralPath { "$Path/$_" }
. - If you want directory-info objects (
[System.IO.DirectoryInfo]
) instead, append
| Get-Item -LiteralPath { "$Path/$_" }
.
Optional reading: Getting folders up to, at, or beyond a certain depth:
Note:
- Even though the solutions below target folders (directories), you can include files too by simply omitting
-Directory
, or target files only by replacing-Directory
with-File
. - For simplicity, the commands implicitly target the current directory.
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:
1 2 3 4 |
$depth = 2 # grandchild directories only Get-ChildItem -Directory -Path ('*/' * $depth) |
- To output full paths, enclose the
Get-ChildItem
command in(...).FullName
or pipe it toSelect-Object -ExpandProperty FullName
. - To output relative paths (e.g.,
folder1/test1/test/testsub
), additional work is needed, because adding-Name
will not work as expected in this case (it will output just the directory names):
1 2 3 4 5 6 7 8 9 10 |
$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:
1 2 3 4 |
$depth = 2 # child and grandchild directories Get-ChildItem -Directory -Depth ($depth - 1) |
- To output full paths, enclose the
Get-ChildItem
command in(...).FullName
or pipe it toSelect-Object -ExpandProperty FullName
. - To output relative paths, simply add the
-Name
switch to theGet-ChildItem
call.
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:
1 2 3 4 5 6 |
$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
.
- To output full paths, replace
Get-Item
withConvert-Path
. - To output relative paths, simply omit the
Get-Item
call.