CMD.exe recursion in batch: Deleting empty subdirectories

      Comments Off on CMD.exe recursion in batch: Deleting empty subdirectories

This article addresses one specific use case for recursion in batch: Recursively looping through a directory (also called a folder) and its sub directories.

Normally, this is a pain, and it took me a while to come up with an algorithm that works.


For recursion to work correctly, you need:

a) Subroutines, and

b) Local variables.

CMD.EXE supports the first one, but local variables are a pain: You can only use SETLOCAL/ENDLOCAL, which prevents any information to be returned to any calling level (except the ERRORLEVEL).

Here is a solution, implemented as a script that recursively deletes empty sub directories of the current directory, and traverses all the way down the directory tree:

@echo off 
rem This pushd is to make sure the script always returns to
rem the directory it was called from
pushd . 
set ABORT= 
call :Process_Dir 
popd 
exit /b
:Process_Dir
  rem If ABORT is set, do not continue, but unwind the call stack.
  rem Errorlevel 1 makes sure the rd command is not attempted
  if defined ABORT exit /b 1

  rem First, process the subdirectories through recursion
  for /d %%I in (*.*) do call :Recurse "%%~I"
  rem If ABORT is set, do not continue, but unwind the call stack
  if defined ABORT exit /b 1

  rem Next, count the file items in the current directory
  rem Actually, I am only interested if there are 0, or 1 (or more)
  rem items in the directory
  set CNT=0
  for /f %%I in ('dir /b') do set CNT=1

  rem Return the CNT flag as an errorlevel to the calling sub 
exit /b %CNT%

:Recurse
  rem If the CD fails, set ABORT and exit immed
  rem !!If the following line shows &, replace it with a real ampersand - it's a
  rem !!  limitation of the syntax highlighter on our web site
  cd "%~1"||(set ABORT=Y&exit /b)
  call :Process_Dir
  set RESULT=%ERRORLEVEL% 
  if defined ABORT exit /b
  cd ..
  rem Based on the result of Process_Dir, delete the subdirectory
  if %RESULT% equ 0 rd "%~1" 
exit /b

That’s it!