19

I'm currently using the following command to list some directories:

dir /b /s /AD > c:\temp\dir_list.txt

This gives me almost the list that I need. But it is way too much data because some folders have lots of subfolders that I don't want to see in my listing.

Is it possible to limit the recursion depth of the command to 3 (for example)?

c:\dir_1\dir_2\dir_3\dir_foo

So I don't want to see the directory dir_foo if I execute the command in the above example in c:\>, but just the dir_n ones.

Maybe without a batch or VB script?

Mofi
  • 46,139
  • 17
  • 80
  • 143
Sauer
  • 1,429
  • 4
  • 17
  • 32
  • 1
    relevant ; https://stackoverflow.com/questions/13249085/limit-get-childitem-recursion-depth – n00b May 18 '18 at 17:30

6 Answers6

23

After all this time (5 years), and I just now stumble on a simple command line one liner that has been available all along. ROBOCOPY has been a standard Windows utility since Vista, and is available to XP via the Windows Resource Kit.

robocopy . . /l /s /njh /njs /ns /lev:4 >c:\temp\dir_list.txt

Explanation

    /L :: List only - don't copy, timestamp or delete any files.
    /S :: copy Subdirectories, but not empty ones.
  /NJH :: No Job Header.
  /NJS :: No Job Summary.
   /NS :: No Size - don't log file sizes.
/LEV:n :: only copy the top n LEVels of the source directory tree.

The /lev:n option includes the root in the count, and you want 3 subdirectory levels, which is why I added 1 to the value.

Processing further

The output is not perfect in that the root folder is included in the output, and each path includes fixed width leading whitespace. You can conveniently eliminate the root path as well as the leading whitespace with FOR /F.

(for /f "skip=2 tokens=*" %A in ('robocopy . . /l /s /njh /njs /ns /lev:4') do @echo %A) >c:\temp\dir_list.txt

The ROBOCOPY output includes an initial blank line, which is why the skip must be 2 instead of 1.

Each path will end with \. I like that feature because it makes it obvious that we are listing folders and not files. If you really want to eliminate the trailing \, then you can add an extra FOR.

(for /f "skip=2 tokens=*" %A in ('robocopy . . /l /s /njh /njs /ns /lev:4') do @for %B in ("%A.") do @echo %~fB) >c:\temp\dir_list.txt

But the command is becoming a bit ungainly to type. It should be easy to incorporate this technique into a utility batch file that takes the root path and level as arguments. You must remember to double the percents if you put the command within a batch script.

TWiStErRob
  • 44,762
  • 26
  • 170
  • 254
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • 1
    hi there! +1 because I like robocopy and your dedication. But I prefer using your first solution (its just more expressive) – Sauer Mar 29 '17 at 11:34
21

I'm sure it is possible to write a complex command that would list n levels of directories. But it would be hard to remember the syntax and error prone. It would also need to change each time you want to change the number of levels.

Much better to use a simple script.

EDIT 5 Years Later - Actually, there is a simple one liner that has been available since Vista. See my new ROBOCOPY solution.

Here is a batch solution that performs a depth first listing. The DIR /S command performs a breadth first listing, but I prefer this depth first format.

@echo off
setlocal
set currentLevel=0
set maxLevel=%2
if not defined maxLevel set maxLevel=1

:procFolder
pushd %1 2>nul || exit /b
if %currentLevel% lss %maxLevel% (
  for /d %%F in (*) do (
    echo %%~fF
    set /a currentLevel+=1
    call :procFolder "%%F"
    set /a currentLevel-=1
  )
)
popd

The breadth first version is nearly the same, except it requires an extra FOR loop.

@echo off
setlocal
set currentLevel=0
set maxLevel=%2
if not defined maxLevel set maxLevel=1

:procFolder
pushd %1 2>nul || exit /b
if %currentLevel% lss %maxLevel% (
  for /d %%F in (*) do echo %%~fF
  for /d %%F in (*) do (
    set /a currentLevel+=1
    call :procFolder "%%F"
    set /a currentLevel-=1
  )
)
popd

Both scripts expect two arguments:

arg1 = the path of the root directory to be listed

arg2 = the number of levels to list.

So to list 3 levels of the current directory, you could use

listDirs.bat . 3

To list 5 levels of a different directory, you could use

listDirs.bat "d:\my folder\" 5
Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • 2
    great thing! Works good. There is a small "bug" though. If it comes to a folder, the system isn't allowed to access, it goes up one directory and works on there, with the old list. But I dont care about that! Thanks anyway! This is exactly what I wanted! – Sauer Sep 19 '12 at 08:16
  • 1
    @Sauer - Great job diagnosing the bug :-) I fixed the code to eliminate the bug. – dbenham Sep 19 '12 at 12:06
  • How do we do that including hidden folders too? – Chef Gladiator Oct 29 '20 at 23:48
  • @ChefGladiator - I think this will work, but it is untested: change the simple `FOR /D` line to `FOR /F "EOL=: DELIMS=" %%F IN ('DIR /A:D') DO (`. Or better yet, use the `ROBOCOPY` technique in my other answer, as it reads system files by default. – dbenham Oct 30 '20 at 01:02
  • @dbenham that has inspired thank you ... the right incantation is `'DIR /B /S /A:HD'` -- that will list just the hidden directories below the current one. – Chef Gladiator Oct 30 '20 at 15:16
  • @ChefGladiator - and if you just use `/A:D` it will list both hidden and non-hidden folders. – dbenham Oct 30 '20 at 21:53
  • @dbenham yes was just about to add that myself. Also, `/A:D-S` , lists all folders but not system folders. But I have noticed if I add `/S` the '-S` is not respected? – Chef Gladiator Oct 31 '20 at 07:59
6

Here is a solution which based on the depth first listing solution of @dbenham,
and enables to set minimum level as well.

@echo off
setlocal
set currentLevel=0
set maxLevel=%2
if not defined maxLevel set maxLevel=1
set minLevel=%3
if not defined minLevel set minLevel=0

:procFolder
pushd %1 2>nul || exit /b
if %currentLevel% lss %maxLevel% (
  for /d %%F in (*) do (
    if %currentLevel% geq %minLevel% echo %%~FF
    set /a currentLevel+=1
    call :procFolder "%%F"
    set /a currentLevel-=1
  )
)
popd

To set the minimum level just provide it as 3rd parameter.
For example: to list from level 2 to level 5, you could use

listDirs.bat target_path 5 2

Alternatively, you can list from the base level by leaving this parameter empty

listDirs.bat target_path 5
elady
  • 535
  • 6
  • 22
3

This is a little enhancement over the solution of dbenham (and elady). It indents output according to depth. It significantly increases readability.

@echo off
setlocal
set currentLevel=0
set maxLevel=%2
if not defined maxLevel set maxLevel=1
set minLevel=%3
if not defined minLevel set minLevel=0

:procFolder
pushd %1 2>nul || exit /b
set "indent=."
if %currentLevel% lss %maxLevel% (
  for /d %%F in (*) do (
    for /l %%i in (1,1,%currentLevel%) do echo|set /p=%indent%
    if %currentLevel% geq %minLevel% echo %%~fF
    set /a currentLevel+=1
    call :procFolder "%%F"
    set /a currentLevel-=1
  )
)
popd

One can set the indentation character in set "indent ...

0

My refined version over elady's one:

  • minLevel by default is maxLevel-1
  • changed order of params: maxLevel, minLevel, curFolder. This way default target_path is current path, more handy. One can call in 3 different ways:
    • listDirs.bat 2 (straight up)
    • listDirs.bat 2 0 (differente minimum level, try and see)
    • listDirs.bat 2 0 target_path (full syntax when needed)
@echo off
setlocal
set currentLevel=0
set maxLevel=%1
if not defined maxLevel set maxLevel=1
set minLevel=%2
if not defined minLevel set /a minLevel=%maxLevel%-1

:procFolder
pushd %3 2>nul || exit /b
if %currentLevel% lss %maxLevel% (
  for /d %%F in (*) do (
    if %currentLevel% geq %minLevel% echo %%~fF
    set /a currentLevel+=1
    call :procFolder %maxLevel% %minLevel% "%%F"
    set /a currentLevel-=1
  )
)
popd
Bernardo Dal Corno
  • 1,858
  • 1
  • 22
  • 27
0
set "drive=C:" 
for /F "usebackq tokens=* delims=" %%a in (`PowerShell.exe -Command "&{Get-ChildItem -Path %drive% -Exclude *Recycle* -Recurse -Depth 2 -Name -Directory}"`) do ( 
    echo.[%%a] 
) 

-Depth 2 shall limit folder recursion to a depth of 3 folders from the root. -Exclude Recycle shall not show the $Recycle.bin folder. -Name makes the command show bare names of the folders; not the station too.

Victor
  • 11
  • 3