Skip to main content

How to Implement a Dry-Run Mode (--dry-run) in a Batch Script

When writing scripts that perform destructive actions, like deleting thousands of files, modifying registry keys, or updating database records, testing is incredibly dangerous. A "Dry-Run" mode (also known as "What-If" mode) allows you to execute your script safely. Instead of actually performing the actions, the script simply "echoes" what it would have done. This allows you to verify your logic, filters, and target paths without risking any data.

This guide will explain how to build a flexible dry-run guard into your Batch scripts.

1. Defining the Dry-Run Flag

The first step is to detect if the user provided the --dry-run (or -d) flag and store that decision in a "Guard" variable.

@echo off
setlocal

:: 1. Detection Logic
set "DryRun=FALSE"
for %%x in (%*) do (
if /i "%%~x"=="--dry-run" set "DryRun=TRUE"
if /i "%%~x"=="-d" set "DryRun=TRUE"
)

if "%DryRun%"=="TRUE" (
echo [INFO] *** DRY RUN ENABLED - NO CHANGES WILL BE MADE ***
)

2. The "Action Command" Variable Pattern

The most professional way to implement a dry-run is to create an "Action" variable that is either empty (Run normally) or contains the word echo (Dry-run).

@echo off
setlocal

:: 1. Detection Logic
set "DryRun=FALSE"
for %%x in (%*) do (
if /i "%%~x"=="--dry-run" set "DryRun=TRUE"
if /i "%%~x"=="-d" set "DryRun=TRUE"
)

if "%DryRun%"=="TRUE" (
echo [INFO] *** DRY RUN ENABLED - NO CHANGES WILL BE MADE ***
)

:: 2. Set the Guard Command
set "Exec="
if "%DryRun%"=="TRUE" set "Exec=echo [DRY-RUN] Would run:"

:: 3. Apply the Guard to destructive commands
%Exec% del "C:\Temp\Logs\*.log" /q
%Exec% rd /s /q "C:\OldData"

echo Task finished.
endlocal

Why this works:

  • Normal Mode: %Exec% is empty. The command becomes del ..., which executes normally.
  • Dry-Run Mode: %Exec% is echo [DRY-RUN].... The command becomes echo [DRY-RUN] Would run: del ..., which only prints text to the screen.

3. Integrating with Native "Dry-Run" Tools

Many powerful Windows tools (like robocopy) have their own built-in dry-run flags. You should map your script's flag to these tool-specific flags for maximum safety.

@echo off
setlocal

:: 1. Detection Logic
set "DryRun=FALSE"
for %%x in (%*) do (
if /i "%%~x"=="--dry-run" set "DryRun=TRUE"
if /i "%%~x"=="-d" set "DryRun=TRUE"
)

if "%DryRun%"=="TRUE" (
echo [INFO] *** DRY RUN ENABLED - NO CHANGES WILL BE MADE ***
)

:: 2. Map to robocopy's native /L flag
set "DryRunFlag="
if "%DryRun%"=="TRUE" set "DryRunFlag=/L"

:: Robocopy /L lists files without copying them
robocopy "C:\Source" "D:\Backup" /MIR %DryRunFlag%

endlocal

4. Using a Subroutine for Complex Commands

When commands contain pipes (|), redirections (>), or other special characters, the simple %Exec% prefix cannot safely wrap them in an echo statement. A subroutine solves this by accepting the entire command as a quoted string.

@echo off
setlocal

:: 1. Detection Logic
set "DryRun=FALSE"
for %%x in (%*) do (
if /i "%%~x"=="--dry-run" set "DryRun=TRUE"
if /i "%%~x"=="-d" set "DryRun=TRUE"
)

if "%DryRun%"=="TRUE" (
echo [INFO] *** DRY RUN ENABLED - NO CHANGES WILL BE MADE ***
)

:: 2. Use the subroutine for complex commands
call :RunCmd "del /q C:\Temp\Logs\*.log"
call :RunCmd "rd /s /q C:\OldData"
call :RunCmd "net stop SomeService"

echo Task finished.
endlocal
goto :eof

:RunCmd
if "%DryRun%"=="TRUE" (
echo [DRY-RUN] Would run: %~1
) else (
%~1
)
goto :eof

How to Avoid Common Errors

Wrong Way: Complex Nested IFs

Doing if "%DryRun%"=="TRUE" (echo del...) else (del...) on every single line makes your script massive and double the length. It also increases the risk that you update the "real" command but forget to update the "dry-run" command.

Correct Way: Use the %Exec% variable pattern (Section 2) for simple commands or the subroutine pattern (Section 4) for complex ones. You only write your command once, and the guard handles the behavior.

Problem: Variable Expansion

If your command contains complex pipe characters (|) or redirects (>), the %Exec% variable might not always capture them correctly in an echo statement.

Solution: Use the :RunCmd subroutine pattern shown in Section 4. By passing the command as a quoted string and expanding it with %~1, you isolate the special characters from the parser during the dry-run echo.

Best Practices and Rules

1. Verification is the Goal

A dry-run is useless if the user doesn't see what's happening. Ensure your echo messages include the full path and the exact flags being used.

2. Guard EVERYTHING Destructive

A dry-run script that deletes a folder but actually restarts a service by mistake is an incomplete failure. Ensure every single command that modifies the system is protected by the %Exec% variable or the :RunCmd subroutine.

3. Log the Dry-Run

Save the output of your dry-run to a file. This allows you to review a list of 1,000 files to be deleted before you run the script for real. myscript.bat --dry-run > check_this.txt

Conclusions

Implementing a dry-run mode is a sign of a mature and cautious developer. It turns your more dangerous automation into predictable, verifiable tools. By using the %Exec% prefix pattern or the :RunCmd subroutine, you gain the ability to "Preview" your system modifications, providing a crucial safety net for both yourself and your end-users. This practice is essential for any professional script that manages file lifecycles or system-wide configurations.