Search code examples
windowsfor-loopcommand-linedirectorylevels

How do I do this ? - Producing a Dir listing by level - not by following structure


I hope that you can save my sanity here. I have been asked to produce a report showing all the files and folders on a windows server. Not you would think a problem - dir /s > report.txt and your done right?

Wrong.

I have been told that the report must be by level and not following the folder structure. To explain. Imagine we have a server laid out like this -

E:
├───Animals
│   ├───Birds
│   ├───Mammals
│   └───Reptiles
├───Cities
│   └───Continents
│       ├───Africa
│       ├───Europe
│       └───North America
└───Plants
    ├───Flowers
    ├───Grasses
    └───Trees

Dir /s would produce -

Directory of E:
23/11/2017  11:57    <DIR>          .
23/11/2017  11:57    <DIR>          ..
23/11/2017  11:46    <DIR>          Animals
23/11/2017  11:51    <DIR>          Cities
23/11/2017  11:52    <DIR>          Plants

               0 File(s)            0 bytes

 Directory of E:\Animals

23/11/2017  11:46    <DIR>          .
23/11/2017  11:46    <DIR>          ..
23/11/2017  11:47    <DIR>          Birds
23/11/2017  11:49    <DIR>          Mammals
23/11/2017  11:50    <DIR>          Reptiles

               0 File(s)              0 bytes

 Directory of E:\Animals\Birds

23/11/2017  11:47    <DIR>          .
23/11/2017  11:47    <DIR>          ..
23/11/2017  11:47                 0 Duck.txt
23/11/2017  11:47                 0 Ostrich.txt
23/11/2017  11:47                 0 Pigeon.txt

               3 File(s)              0 bytes

 Directory of E:\Animals\Mammals

23/11/2017  11:49    <DIR>          .
23/11/2017  11:49    <DIR>          ..
23/11/2017  11:48                 0 Aardvark.txt
23/11/2017  11:48                 0 cat.txt
23/11/2017  11:48                 0 dog.txt

               3 File(s)              0 bytes

 Directory of E:\Animals\Reptiles

23/11/2017  11:50    <DIR>          .
23/11/2017  11:50    <DIR>          ..
23/11/2017  11:49                 0 Iguana.txt
23/11/2017  11:50                 0 Sand Lizard.txt

               2 File(s)              0 bytes

and so on

But they want -

Directory of E:

23/11/2017  11:57    <DIR>          .
23/11/2017  11:57    <DIR>          ..
23/11/2017  11:46    <DIR>          Animals
23/11/2017  11:51    <DIR>          Cities
23/11/2017  11:52    <DIR>          Plants

               0 File(s)            0 bytes

Directory of E:\Animals

23/11/2017  11:46    <DIR>          .
23/11/2017  11:46    <DIR>          ..
23/11/2017  11:47    <DIR>          Birds
23/11/2017  11:49    <DIR>          Mammals
23/11/2017  11:50    <DIR>          Reptiles

               0 File(s)              0 bytes

Directory of E:\Cities

23/11/2017  11:51    <DIR>          .
23/11/2017  11:51    <DIR>          ..
23/11/2017  11:51    <DIR>          Continents

               0 File(s)              0 bytes

Directory of E:\Plants

23/11/2017  11:52    <DIR>          .
23/11/2017  11:52    <DIR>          ..
23/11/2017  11:51    <DIR>          Flowers
23/11/2017  11:51    <DIR>          Grasses
23/11/2017  11:51    <DIR>          Trees

               0 File(s)              0 bytes

etc...

I have been told to manually list each folder in windows explorer and then screen print each one into a word document.

I mean what!??? There are over 14,000 folders! What did I do to deserve this?! Did I die in the night and not notice? Isn't Hell supposed to be full of flames and dudes wondering around carrying pitchforks??

I am not that familiar with the windows command line, but believe that there are for loops? Could using one of these and somehow looping through the levels be a solution? Is there a solution? (Oh How I hope there's a solution!).

Please someone save me from eternal screen print hell!


-Edit 1-

I have been reading the various web pages on the command line, and while I still do not understand the For /f command I have come across popd and pushd, which allow you to store the current directory on a virtual stack. Since my problem is that I need to somehow modify the behaviour of the dir command so that it lists folders by level and not by following the obvious folder structure I'm wondering if there is any way that I can use pushd and popd to do this?

Playing with the dir command has enabled me to strip the files out to be left with a list of directories (dir *. /s /b) but this still tries to follow the structure, in My example dir *. /s /b gives -

E:\Animals
E:\Cities
E:\Plants
E:\Animals\Birds
E:\Animals\Mammals
E:\Animals\Reptiles
E:\Cities\Continents
E:\Cities\Continents\Africa
E:\Cities\Continents\Europe
E:\Cities\Continents\North America
E:\Plants\Flowers
E:\Plants\Grasses
E:\Plants\Trees

I need to somehow get all the directories with the same number of levels grouped - so move the E:\plants\xxxx ones to below E:\Cities\Continents . I have no idea if I am any nearer a solution, sorting the output of dir /s /b does not work so it looks like I am going to have to somehow parse the output of dir /s /b.


-Edit 2 -

If I was doing this in a high level language - rather than a batch file - I'd take the output of dir /s /b and set a variable -say x - to 1. I'd then run through the dir output and copy any lines with x \'s in them to a new file. At the eof I'd increment x and run though the file again (with x as 2 this time it would pick out all the lines with 2 \'s in them). I'd continue to do so until no lines had x \'s in them and that would give me a file with the needed order. I'd then run through the new file performing a simple dir for each entry.

Thing is I have no idea how to do the above in a batch file - or even if its possible and I do not have access to a high level language for this project. I don't even think they would let me use Word vb and its years since I did any Visual Basic programming in any case.


Solution

  • @echo off 
    setlocal enabledelayedexpansion
    
    (for /f "delims=" %%a in ('dir /b /s /ad') do (
      set "line=%%a"
      set "line=!line:\= !"
      set depth=100
      for %%b in (!line!) do set /a "depth +=1"
      echo !depth!#%%a
    )) >temp
    for /f "tokens=1* delims=#" %%a in ('sort temp') do echo %%b
    

    Some of the used "tricks":

    • Your find /c approach fails, because it's not finding occurences, but lines with the searchstring (it doesn't matter if there is one or several findings in one line)
    • I used dir's switch /ad to show directories only.
    • for the counting, I use a for loop (for every word increase the counter by one)
    • Therefore, I first remove spaces from the string, then replace every \ with a space.
    • Starting the count with 100 instead of 0 may sound strange, but it enables the code to work with depths > 10 (sort sorts strings, so 4 would be greater than 12)
    • Sadly, I have to use a temp file (piping does re-enable echo on)