Performing an operation in each subdirectory of a directory tree from batch


To execute a command in each subdirectory of a directory tree from a batch file, you can adapt the following:

for /f "delims=" %%i in ('dir /ad/s/b') do echo %%i

(If you want to play with this command from the command prompt, then undouble the percent signs.)

The /F option enables various special behaviors of the FOR command. The most important change is that a string in single-quotation marks causes the contents to be interpreted as a command whose output is to be parsed. (This behavior changes if you use the usebackq option, but I’m not using that here.) Therefore, the FOR command will run the dir /ad/s/b command and parse the output. The dir /ad/s/b command performs a recursive listing of only directories, printing just the names of the directories found.

The option we provide, delims= changes the default delimiter from a space to nothing. This means that the entire line is to be read into the %i variable. (Normally, only the first word is assigned to %i.) Therefore, the FOR loop executes once for each subdirectory, with the %i variable set to the subdirectory name.

The command request to be performed for each line is simply echoing the directory name. In real life, you would probably put something more interesting here. For example, to dump the security descriptor of each directory (which was the original problem that inspired this entry), you can type this on the command line:

for /f "delims=" %i in ('dir /ad/s/b') do cacls "%i" >>"%TEMP%\cacls.log"

Nitpicker's corner

I doubt anybody actually enjoys working with batch files, but that doesn't mean tips on using it more effectively aren't valid.

Comments (46)
  1. Sohail says:

    Batch files! NOOOOOOOOOOOOOOOOOOOoooooooooooooooooo……….

    splat.

  2. Maxim says:

    Alternatively:

    for /r /d %%i in (*) do echo %%i

    [Nice, but there’s more to the “dir” trick than meets the eye. I’ll come back to this. -Raymond]
  3. Jules says:

    Or the other alternative (using cygwin)

    find -type d -exec echo ‘{}’ ‘;’

    I do think the cygwin way of doing this kind of thing is substantially friendlier…

    [Woo-hoo, now I have two problems. -Raymond]
  4. Dave says:

    I prefer to use Windows Script Host for these kind tricks. The clumsiness of FileSystemObject can be hidden with some functions and you have all of Javascript (or VBScript) at your disposal for string operations. All the infrastructure is already down on the drive, at least since Windows 95.

  5. J says:

    "You make it sound like you have to re-install perl every time you want to write a perl script."

    And what if you do?  For instance, you’re trying to debug a problem on a customer’s PC that you’ve never seen before.

    "Umm, do you mind if I install perl, Visual Studio, and Photoshop first before I can start debugging this issue?"

    You know, you can use your head and figure out whether it really does cause you two problems instead of taking Raymond’s word as gospel instead of advice.  If it turns out it doesn’t cause you two problems, then good for you!  You deserve a treat for figuring that out yourself.

  6. El Guapo says:

    This is a microsoft blog right? You never tried powershell I guess? Maybe you have heard of it. Its something your company makes.

  7. josh says:

    Might as well mention "for /r /d %I in (.) do @echo %I" as well, which seems to be the method that whoever wrote the help text for the for command recommends.  You get names that end in ., but it lists directories that (*) misses.

  8. Aaron says:

    Quoting Abraham Lincoln for a discussion on corporate IT – brilliant.  Totally misunderstanding the quote and using it in absolutely the wrong context – priceless.

  9. bionicbee says:

    Is there any good documentation on the syntax for batch files? We have some homegrown scripts to do various things but some things are extremely hard to figure out, like how to deal with parentheses in strings, which comes up when trying to parse a PATH envvar on x64 systems for example.

    It’s humiliating (but mostly frustrating) for an expert C++ programmer to spend three hours figuring out how to append a string to another… in the end I wrote a .net app in five minutes to solve the problem.

  10. Dave says:

    "This is a microsoft blog right? You never tried powershell I guess?"

    I am pretty sure Raymond was looking to work with the tools already available on the island. That’s why I mentioned WSH as an alternative as well. If you’re going to download and MSH, Perl, Python, whatever on 100 or 1000 computers to solve a simple admin problem then you are wasting time.

  11. mikeb says:

    > Is there any good documentation on the syntax for batch files?

    Not really – there’s 3 decent resources for Windows batch scripting that I know of:

    1) cmd.exe’s help facility (just type "help" or use the /? option for the command you want specific help on).

    2) Tim Hill’s book "Windows NT Shell Scripting" (http://www.amazon.com/dp/1578700477/)

    3) Google

    Unfortunately, cmd’s batch syntax seems to have grown more or less organically, rather than been designed.  In particular the behavior for quoting characters and strings can be strange an unintuitive. So it can be quite painful to do much in the way of string manipulation.

  12. Mihai says:

    Thanks for the tip, batch files are still good for a lot of things.

    And when I need something more powerful, I have discovered a cool tool a while ago: KiStart http://www.kixtart.org/index.asp

    Needs no installation (just drop 2 files with your script), and has a scripting language, can do ole automation, registry access, reading files, etc, and.

    Sure, Perl, Awk, PowerShell, have their place and I use them. But knowing more tools allows you to choose the best for the job.

  13. RyanBemrose says:

    @bionicbee: If all you want is documentation on commands, search for "command processor" in MSDN, or try "help whatever" at the command-line.

    For more in-depth stuff, I highly recommend "Windows NT Shell Scripting" by Tim Hill.

    http://www.amazon.com/Windows-Shell-Scripting-Timothy-Hill/dp/1578700477

  14. mikeb says:

    > You never tried powershell I guess? Maybe you have heard of it.

    What’s your point?  The fact that PowerShell exists makes old-fashioned batch files useless now?  All those little .cmd files in my util directory have not seemed to notice.

  15. Fox Cutter says:

    This is a usefull tip. I’m always surprised by how much you can do with the command language in Windows. There’s quite a lot you can do using it without having to go into other applications.

  16. cmov says:

    “[You have to install it on every computer that will run your script, which in a corporate setting can be hundreds or thousands. At some point, the trade-off will be worth it, and you can ignore all my batch file tips. Until then, the tips are there. -Raymond]”

    But for your tips, I would need to install Windows.

    Eek.

    [If your machines aren’t already running Windows, then yes, you would have to install Windows, too, and that would be quite a burden. I guess I have to add a new sentence to the nitpicker’s corner now. -Raymond]
  17. I do this kind of thing all the time. There’s a certain punk cachet in doing cool things with batch that I enjoy. Sort of a question (although this should maybe have gone to the suggestion box): do you know why usebackq isn’t the default? It seems slightly odd to me that since a “syntax” for “run a command and get its output” already existed (backticks) that cmd.exe implemented a different one (single quotes) and then put in the backticks as a config option. This might not be a Raymond question at all; I don’t know whether the command shell comes under the aegis of the shell team, ironically enough!

    [You have your history backwards. -Raymond]
  18. KJK::Hyperion says:

    Stuart: thank GOD it isn’t the default. It is a PAIN to type backquotes on certain keyboard layouts. Also I think single quotes came first and the alternate syntax was added later

  19. Raymond: ah, OK, if I’ve got it backwards then I was wrong. Not sure what I’ve got backwards, mind :)

    KJK::Hyperion: backticks are awkward to type? I clearly don’t have as much experience of alternate keyboard layouts as I need, then!

  20. KJK::Hyperion says:

    Stuart: Alt-096 on an Italian layout…

  21. coldacid says:

    Or the other alternative (using cygwin)

    >

    find -type d -exec echo ‘{}’ ‘;’

    >

    I do think the cygwin way of doing this kind of

    thing is substantially friendlier…

    I dunno… I’m a big fan of Cygwin myself, and I think that the way Raymond showed was far more readable.

  22. Maurits says:

    You have to install it on every computer that will run your script, which in a corporate setting can be hundreds or thousands.

    To be fair, in a corporate setting, installing Monad on hundreds or even thousands of computers is only about ten times as difficult as setting it up on one computer.

  23. Nick says:

    I realize it’s not exactly the point of this post, but for anyone who is interested, you might check out the free AutoIt (http://www.autoitscript.com/autoit3/). It has an incredibly full-featured API that lets you do nearly anything you’d want to on Windows. The syntax is a lot like Visual Basic, and you can compile the scripts into a standalone executable, so no need to install libraries, etc.

    In any case, it has been very helpful for me in the past.

  24. AltMihai says:

    Also, http://www.robvanderwoude.com/batchfiles.html is an invaluable resource for those who would invest some time in learning the Windows command line.

  25. Adam says:

    [Woo-hoo, now I have two problems. -Raymond]

    Yes, but if you solve one of them *once*, it makes *all your other* problems easier to solve from then on. If you’d installed cygwin/perl/monad/whatever last March when it was originally suggested, you’d have *already* solved that problem, and would be able to solve this one more easily.

    You make it sound like you have to re-install perl every time you want to write a perl script.

    “Give me six hours to chop down a tree and I will spend the first four sharpening the axe.”

    – Abraham Lincoln

    Using batch files to accomplish all your automation goals is like trying to cut down all your trees with a rusty axe. If you could just take a day to leave the forest, find civilisation, and buy a damn chainsaw, you’ll make the time up later. Honest.

    “No, no, I can’t take the time out to go and find proper tools – I’m too busy doing it slowly!”

    [You have to install it on every computer that will run your script, which in a corporate setting can be hundreds or thousands. At some point, the trade-off will be worth it, and you can ignore all my batch file tips. Until then, the tips are there. -Raymond]
  26. Paul says:

    “You make it sound like you have to re-install perl every time you want to write a perl script.” – Adam

    You are assuming you got the go ahead to install <whatever your technology of choice is by whatever vendor of choice> in the first place – as Raymond points out in the linked article this is not always trivial or even possible.

    If you can dump the permissions of a file system tree on a production server to a file without installing and other software (which could introduce new security issues, training requirements etc.) then surely one line typed in at a cmd.exe prompt is a winner. After all this was only a ‘tip’ and not an attempt to promote using batch files in any way.

    [I can imagine the support call now. “Hi, is there a quick way to dump the ACLs on all the directories on our server into a file?” “Why yes there is. First, install cygwin.” *Click*. -Raymond]
  27. Peter Ritchie says:

    Outstanding the nitpickability of these folks.  Raymond even added "I doubt anybody actually enjoys working with batch files…", meaning for those of use who *HAVE* to use batch files and cannot use anything else (locked-down PC, corporate standards, least common-denominator, etc. etc. etc.) then, as Raymond says, "doesn’t mean tips on using it more effectively aren’t valid."

    Sheesh.

    Looks like Raymond needs a nitpicker’s prelude to explain why he’s talking about what he’s talking about.

  28. KJK::Hyperion says:

    Ha! I used "tokens=*" instead of "delims=". And I use uppercase letters for the variable name, to make tilde expansions non-ambiguous. And you should prefix the "do" command with an @, or the command will be echoed. In other words, I’d use:

    for /f "tokens=*" %%I in (‘dir /ad/s/b’) do @echo %%I

    Also, how to perform an infinite loop: for /l %I in (1,0,1) do …

    Finally a little warning: you can make a pipeline of "for" loops (and, indeed, any built-in command), but be aware that, if there are more than two in the same pipeline, each will be run in a subprocess. The /V and /E options won’t be passed onto the cmd.exe subprocess, I guess as a bug, and this might screw with !VARIABLE! expansion; in that case, you can sacrifice syntactic sugar by replacing the offending "for" loop with an explicit cmd /e:on /v:on /c "for …"

  29. Mike C says:

    Back in the days of DOS, I used a little utility called "Sweep" (http://short.stop.home.att.net/freesoft/batch2.htm#sweep) that did the same thing.

    For example, "sweep del *.bak" would delete *.bak files from the current directory and all subdirectories under it.

  30. Maurits says:

    > I doubt anybody actually enjoys working with batch files

    /me sheepishly raises hand…

    http://channel9.msdn.com/ShowPost.aspx?PostID=51329

    http://channel9.msdn.com/ShowPost.aspx?PostID=189460

    My particular favorite has to be xargs.bat, which allows the charmingly anaOStic

    dir /b /a:-d *.cpp | xargs start

    or

    dir /b /a:-d *.cpp | xargs /addquotes start ""

    (to placate start’s weird argument syntax)

  31. telcor says:

    > You have to install it on every computer that will run your script, which in a corporate setting can be hundreds or thousands.

    To be fair, in a corporate setting, installing Monad on hundreds or even thousands of computers is only about ten times as difficult as setting it up on one computer.

    Which supposes one already went through the bureaucracy to gain approval for installing software domain-wide

  32. Richard says:

    Adam, I won’t go over how wrong your post as a whole is, but the quote you use stands out. I believe the meaning is that if you give people too much time for a task they’ll waste time on things that don’t actually help with the task.

    If you have someone who has unlimited free time because they have no job and don’t have to pay rent or buy food an acceptable solution might be to create a new programming language, build a community, and have the community come up with tools to solve their problem. People who have limited time have to devote all of it to actually solving the problem, not configuring new platforms.

  33. Steve Miller (creator of Dependency Walker) has a DOS toolbox application collection. One of these applications is global.exe, which let’s you run a command for each subdirectory with some additional options. See his applications page for more details:

    http://www.stevemiller.net/apps/

  34. Sven Groot says:

    Ah, batch files. I can never quite decide whether to feel nostalgic about them or just install PowerShell and say "good riddance". :P After spending a brief time (like, 30 minutes) with Exchange 2007 and PowerShell, the latter is more likely. :)

    They are a good solution sometimes when you can’t go off installing new stuff, but WScript also fits that description and is a lot more flexible. Bottom line, unless what I need to do really is nothing more than a simple sequence of commands, I tend to not use batch files.

    Anti-nitpickers corner: this isn’t to say the tips aren’t useful and/or you should stop giving them.

  35. Dewi Morgan says:

    I love tips like this – I still have "which.bat" from one of the earlier tips posts, where people argued over which was the most elegant solution.

    I admit I don’t yet see the appeal of the

    "for /r /d %I in (.) do @echo %I"

    over

    for /f "delims=" %%i in (‘dir /ad/s/b’) do echo %%i

    …but that just makes me look forward more to the "I’ll come back to this." post.

    I notice very few people saying in the C posts "OMGWTFBBQ use Perl/Python/Java/Bash!" – why should this post be different? It’s a post about cmd commands, it’s blatantly obvious that there are alternatives out there, but that’s no more the point than in the C language posts.

  36. cmov says:

    "[If your machines aren’t already running Windows, then yes, you would have to install Windows, too, and that would be quite a burden. I guess I have to add a new sentence to the nitpicker’s corner now. -Raymond]"

    No, it’s allright -_-‘ I was just kinda silly :p

  37. Anon says:

    One trick I liked in Dos batch files is that you can have functions, you just need to simulate them with recursion, e.g.

    if "%1"=="/foo" goto foo

    if "%1"=="/bar" goto bar

    @rem call function foo

    call %0 /foo fooargs

    @rem call function bar

    call %0 /bar barargs

    goto exit

    :foo

    @echo foo – args %2

    goto exit

    :bar

    @echo bar – args %2

    goto exit

    :exit

    The idea of a language which forces you to do this is oddly charming.

    Though it turns out that in Windows, you can actually do this

    @rem call function foo

    call :foo fooargs

    @rem call function bar

    call :bar barargs

    goto :EOF

    :foo

    @echo foo – args %2

    goto :EOF

    :bar

    @echo bar – args %2

    goto :EOF

    Call now allows you to call labels as :label, and :EOF is a built in end of file label. Now that’s progress ;-)

  38. Hayden says:

    CMD.EXE’s "help" command is a riot. Somebody has carefully (and reasonably completely) documented all of the cute and fun bits of the script language. It just most people don’t know it’s there ("help" on DOS5/6 wasn’t wildly useful) and some of the more magically useful bits aren’t where you might expect (PUSHD creating temporary drive mappings for UNC paths, for example).

  39. 640k says:

    Raymond is rejecting cygwin not on technical basis. It’s because his employer doesn’t want him to appreciate other vendors applications.

    [You may notice that I don’t appreciate Microsoft applications either. I’m an equal-opportunity shunner. -Raymond]
  40. David Walker says:

    I think Raymond wasn’t "rejecting" CYGWIN, but if you read the linked article "now you have two problems" href=http://blogs.msdn.com/oldnewthing/archive/2006/03/22/558007.aspx all will be clear.

    I hope the link works.

  41. Legolas says:

    Hey thanks for this. I don’t think I realized that you could specify no delims at all by using that option, when I wrote my last batch file. Time to go back and see if this simplifies things, it might well.

  42. Cooney says:

    Which supposes one already went through the bureaucracy to gain approval for installing software domain-wide

    Not all corporate structures are completely hidebound. Installing something like perl shouldn’t be all that hard, given its long history and the rather minimal security risks of having it there when you aren’t using it.

  43. arnshea says:

    Thanks for the tip!  Another one I’ve found useful is enabling delayed environment variable expansions (cmd /V:on).  That plus xcopy /D makes selectively deploying directory contents across a network onto an XPe target a cinch.

    For nostalgia they’re stored in c:bin.  The XP command shell may not be bash but it’s got quite a few hidden gems for the "not quite worth a program" level of automation.

  44. Ema says:

    Ooc, I really like Raymond’s intellectual honesty…if every guy at his workplace were like him there where no FUD against other OSes and Windows would be a better product.

    End of Ooc,

    Cheers,

    Ema! ;-)

  45. Jim Vaglia says:

    I didn’t receive Access with my copy of Office for XP so why did I receive a Microsoft Access security update? I removed Windows medea 6 security update so shouldn’t streams play just fine in other media players?

    [Did you try using the for command? -Raymond]

Comments are closed.