Skip to main content

How to Implement a Verbose Mode (--verbose) in a Batch Script

A verbose mode is a runtime option that makes a script print additional diagnostic information as it runs. By default the script stays quiet, showing only essential output, but when the user passes a --verbose flag the script reveals every step it takes, every file it touches, and every decision it makes. This pattern is standard in professional command-line tools and is invaluable for debugging automated workflows.

In this guide, we will explore how to implement a robust --verbose flag in a Batch Script, covering argument parsing, conditional output, and integration into real-world scripts.

Understanding the Pattern

The verbose mode pattern has three parts:

  1. Parse the command line for a --verbose (or -v) flag.
  2. Store the result in a variable (e.g., VERBOSE=1).
  3. Guard every diagnostic echo with an if check on that variable.

Method 1: Simple Verbose Flag

@echo off
setlocal

set "VERBOSE=0"

:: Parse arguments
:parse
if "%~1"=="" goto :main
if /i "%~1"=="--verbose" set "VERBOSE=1"
if /i "%~1"=="-v" set "VERBOSE=1"
shift
goto :parse

:main
if %VERBOSE%==1 echo [VERBOSE] Verbose mode enabled.

echo Starting process...
if %VERBOSE%==1 echo [VERBOSE] Checking prerequisites...

:: Simulate work
if %VERBOSE%==1 echo [VERBOSE] Reading configuration from config.ini
if %VERBOSE%==1 echo [VERBOSE] Connecting to server...

echo Process complete.
pause

Normal output:

Starting process...
Process complete.

Output with --verbose:

[VERBOSE] Verbose mode enabled.
Starting process...
[VERBOSE] Checking prerequisites...
[VERBOSE] Reading configuration from config.ini
[VERBOSE] Connecting to server...
Process complete.

Method 2: Using a CALL-Based Helper

Repeating if %VERBOSE%==1 echo ... on every line is verbose in itself. A cleaner approach uses a helper label:

@echo off
setlocal

set "VERBOSE=0"
if /i "%~1"=="--verbose" set "VERBOSE=1"
if /i "%~1"=="-v" set "VERBOSE=1"

call :vlog "Verbose mode check complete."

echo Starting backup...
call :vlog "Source directory: C:\Data"
call :vlog "Destination: \\Server\Backups"

robocopy C:\Data \\Server\Backups /mir /r:2 /w:3 /np >nul 2>&1
set "result=%errorlevel%"

call :vlog "Robocopy exit code: %result%"

if %result% leq 3 (
echo Backup complete.
) else (
echo [ERROR] Backup failed.
)

call :vlog "Script finished at %time:~0,8%"
pause
exit /b

:vlog
if %VERBOSE%==1 echo [VERBOSE] %~1
exit /b
tip

The :vlog subroutine centralizes all verbose output through a single label. This makes it easy to change the prefix (e.g., from [VERBOSE] to [DEBUG]), add timestamps, or redirect verbose output to a file, all in one place.

Method 3: Verbose with Multiple Levels

Some tools support multiple verbosity levels (-v, -vv, -vvv):

@echo off
setlocal

set "VERBOSITY=0"

:parse
if "%~1"=="" goto :main
if /i "%~1"=="-v" set "VERBOSITY=1"
if /i "%~1"=="-vv" set "VERBOSITY=2"
if /i "%~1"=="-vvv" set "VERBOSITY=3"
if /i "%~1"=="--verbose" set "VERBOSITY=1"
if /i "%~1"=="--debug" set "VERBOSITY=3"
shift
goto :parse

:main
call :log 1 "Script started."
call :log 2 "Working directory: %cd%"
call :log 3 "Environment: PATH=%PATH%"

echo Processing files...
call :log 1 "Scanning source directory..."
call :log 2 "Found 42 files to process."
call :log 3 "File list: a.txt, b.txt, c.txt, ..."

echo Done.
pause
exit /b

:log
if %VERBOSITY% geq %1 echo [V%1] %~2
exit /b
LevelFlagShows
0(default)Essential output only
1-vHigh-level progress
2-vvDetailed operations
3-vvv or --debugEverything, including internal state

Method 4: Verbose Mode with Log File

Write verbose output to both the screen and a log file:

@echo off
setlocal

set "VERBOSE=0"
set "LOGFILE=script_%date:~-4%%date:~4,2%%date:~7,2%.log"

:parse
if "%~1"=="" goto :main
if /i "%~1"=="--verbose" set "VERBOSE=1"
if /i "%~1"=="-v" set "VERBOSE=1"
shift
goto :parse

:main
call :vlog "=== Script started at %date% %time:~0,8% ==="
call :vlog "User: %USERNAME% Machine: %COMPUTERNAME%"

echo Performing system check...
call :vlog "Checking disk space..."

for /f "tokens=3" %%F in ('dir /-c C:\ ^| findstr "bytes free"') do (
call :vlog "Free space on C: %%F bytes"
)

call :vlog "Checking network..."
ping -n 1 8.8.8.8 >nul 2>&1
if %errorlevel%==0 (
call :vlog "Network: OK"
) else (
call :vlog "Network: UNREACHABLE"
)

echo System check complete.
call :vlog "=== Script finished ==="

if %VERBOSE%==1 echo Log saved to: %LOGFILE%
pause
exit /b

:vlog
:: Always write to log file
echo [%time:~0,8%] %~1 >> "%LOGFILE%"
:: Only print to screen if verbose
if %VERBOSE%==1 echo [%time:~0,8%] %~1
exit /b

Method 5: Real-World Deployment Script with Verbose

@echo off
setlocal enabledelayedexpansion

set "VERBOSE=0"
set "SOURCE="
set "DEST="

:parse
if "%~1"=="" goto :validate
if /i "%~1"=="--verbose" (set "VERBOSE=1" & shift /1 & goto :parse)
if /i "%~1"=="-v" (set "VERBOSE=1" & shift /1 & goto :parse)

if not defined SOURCE (set "SOURCE=%~1" & shift /1 & goto :parse)
if not defined DEST (set "DEST=%~1" & shift /1 & goto :parse)
shift /1
goto :parse

:validate
if not defined SOURCE (
echo Usage: %~nx0 ^<source^> ^<destination^> [--verbose]
exit /b 1
)
if not defined DEST (
echo Usage: %~nx0 ^<source^> ^<destination^> [--verbose]
exit /b 1
)

call :vlog "Source: !SOURCE!"
call :vlog "Destination: !DEST!"

:: Step 1: Verify source
echo [1/3] Verifying source...
if not exist "!SOURCE!" (
echo [ERROR] Source not found: !SOURCE!
exit /b 1
)
call :vlog "Source exists."

:: Step 2: Copy
echo [2/3] Deploying...
call :vlog "Running robocopy..."
robocopy "!SOURCE!" "!DEST!" /mir /r:2 /w:3 /np /ndl /nfl >nul 2>&1
set "rc=!errorlevel!"
call :vlog "Robocopy exit code: !rc!"

if !rc! leq 3 (
echo Deployed.
) else (
echo [ERROR] Deploy failed.
exit /b 1
)

:: Step 3: Verify
echo [3/3] Verifying deployment...
call :vlog "Comparing file counts..."

set "src_count=0"
for /f %%C in ('dir /b /a-d "!SOURCE!" 2^>nul ^| find /c /v ""') do set "src_count=%%C"

set "dst_count=0"
for /f %%C in ('dir /b /a-d "!DEST!" 2^>nul ^| find /c /v ""') do set "dst_count=%%C"

call :vlog "Source files: !src_count!, Dest files: !dst_count!"

echo.
echo [DONE] Deployment complete.
exit /b 0

:vlog
if %VERBOSE%==1 echo [V] %~1
exit /b

Usage examples:

deploy.bat C:\Build\Output \\Server\WebApp
deploy.bat C:\Build\Output \\Server\WebApp --verbose
deploy.bat C:\Build\Output \\Server\WebApp -v

Common Mistakes

The Wrong Way: Using echo for Everything

:: WRONG - Always prints debug info, no way to turn it off
echo Connecting to database...
echo Query: SELECT * FROM users
echo Result: 42 rows
echo Formatting output...

Output:

Connecting to database...
Query: SELECT * FROM users
Result: 42 rows
Formatting output...

Without a verbose flag, users see unnecessary internal details on every run. This clutters the output and makes it harder to spot actual errors or results.

The Wrong Way: Forgetting Case Insensitivity

:: WRONG - Only matches exact lowercase
if "%~1"=="--verbose" set "VERBOSE=1"
:: Fails with --Verbose, --VERBOSE, -V

Always use if /i for flag comparisons so that --VERBOSE, --Verbose, and --verbose all work.

Best Practices

  1. Default to quiet: Scripts should be silent by default, printing only essential results.
  2. Use /i for flag matching: Accept flags in any case.
  3. Centralize verbose output: Use a :vlog subroutine instead of repeating if checks everywhere.
  4. Support both -v and --verbose: Follow common CLI conventions that users already know.
  5. Combine with log files: Write verbose output to a log file always, and to screen only when requested.

Conclusion

Implementing a verbose mode in a Batch Script follows the pattern of parsing a --verbose or -v flag into a variable and guarding diagnostic output with a conditional check. By centralizing verbose output through a :vlog subroutine, supporting multiple verbosity levels, and optionally writing to both screen and log file, scripts become professional tools that provide just enough information when things work and full diagnostic detail when troubleshooting is needed.