Copycat has done it again

by zemion
Posted in Tech Talk

If you never had the need or necessity to copy a whole first level of directory structure without the files contained in Windows, this post isn't for you. Stop reading right now! If on the other hand you ever had to do that or something similar (e.g. copy the whole tree without files) - keep reading, you might find it useful.

A colleague came to me asking if I could help her copy a whole bunch of directories (for unified naming purposes). She already had considered the easy way: copy the WHOLE directory, delete all subdirectories manually. Too time consuming, yes, but we talk about a directory tree with approximately 57 gigabyte of data, carefully put in 50,000 files and 8,500 folders, of which 178 were on the top level (and thus needed in the final result). When additionally you have to copy the structure over to a different network drive (which we had to), we can agree that it's highly impractical.

So I proposed a different approach: Use one console command to copy over the folder struture only, and another to delete everything unnecessary (e.g. subdirectories) in a second step. Here we go ...

The command xcopy is much more versatile than the good ol' cp. With xcopy /s /e /t we copy subdirectories (/s), even if they aren't empty (/e) and leave out the files (e.g. copy the directories only, /t). That's it for step one. It takes some time, but you will end up with a complete copy of your original directory structure without the contained files. First step: done in next to no time (compared to copying the complete content)!

On to the second task. Deleting directories with rmdir or rd deletes directories (even recursively), but has one huge disadvantage: it doesn't accept wildcards (e.g. something like rd ** will fail). But we can do something else, namely looping through all directories, deleting the subdirectories contained. But to delete a non-empty directory, rd /s will suffice. Adding /q puts the command in quiet mode.

This is done by a simple for /f "delims=" %i in ('dir /a:d /b *') do ... command. Explanation: for /f loops through a text file (or in our case, a console output) line by line; the option "delims=" tells the command to use no delimiters (thus including whitespaces in the pathname). The dir /a:d /b * lists directories only (/a:d for attributes:directory) and prints them in short form (e.g. without date, size etc., /b). So what we will get is a loop over all first level directories in the current working directory we can use in the do part utilizing variable %i.

Now remember we want to delete everything except the first level subdirectories. So we repeat the process and end up with the following one-liner:

for /f "delims=" %i in ('dir /a:d /b *') do (for /f "delims=" %j in ('dir /a:d /b "%i"*') do rd /s /q "%i""%j")

If you followed the text above, it should be simple to understand what's happening. We loop over all folders in the current directory, using the output (e.g. every single first level subdirectory) to loop over every contained directory (e.g. second level subdirectory) and use both outputs (variables %i and %j) to build a directory name to delete recursively. Result: Everything below the first level will be gone and we are left with our top level directory structure.

Did you like this text? Maybe it even helped you? I certainly hope it did. If you want to show your appreciation, you can send me a tip.