cheat sheet

rmdir / rd

Delete empty or entire directory trees from the Windows command prompt. Covers the /S and /Q flags, force-removing read-only trees, and safe deletion patterns.

rmdir / rd — Remove Directory

What it is

rmdir (also rd) is a built-in cmd.exe command that removes a directory from the filesystem. By default it only removes empty directories; the /S flag recursively deletes an entire directory tree including all files and subdirectories. Like del, it bypasses the Recycle Bin — deletions are permanent unless a shadow copy or backup exists. rd is a shorthand alias for rmdir; both are interchangeable.

Availability

rmdir and rd are built into cmd.exe on every Windows version. PowerShell equivalents: Remove-Item -Recurse -Force (aliased rmdir, rd, rm).

cmd
rmdir /?

Output:

css
Removes (deletes) a directory.

RMDIR [/S] [/Q] [drive:]path
RD [/S] [/Q] [drive:]path

Syntax

The target is a directory path. Without /S, the directory must be empty. Paths with spaces must be quoted.

cmd
rmdir [/S] [/Q] path
rd [/S] [/Q] path

Output: (none on success; error if directory not empty or not found)

Essential options

SwitchMeaning
/SRemove the directory tree including all files and subdirectories
/QQuiet mode — no confirmation prompt when using /S

Removing an empty directory

Without /S, rmdir only succeeds if the directory is already empty.

cmd
rmdir C:\Temp\emptydir

Output: (none — exits 0 on success)

cmd
rem rd is the same command
rd C:\Temp\emptydir

Output: (none — exits 0 on success)

cmd
rem Fails if directory is not empty
rmdir C:\Projects\myapp

Output:

csharp
The directory is not empty.

Removing a directory tree

/S deletes the directory, all files inside it, and all subdirectories recursively. In interactive mode it prompts for confirmation; /Q suppresses that.

cmd
rem Interactive — prompts once
rmdir /S C:\Temp\OldBuild

Output:

java
C:\Temp\OldBuild, Are you sure (Y/N)? Y
cmd
rem Silent recursive delete — standard for scripts
rmdir /S /Q C:\Temp\OldBuild

Output: (none — exits 0 on success)

Removing the current directory

You cannot remove the current working directory while inside it. Change to a parent first.

cmd
cd C:\Projects\myapp
rmdir /S /Q C:\Projects\myapp

Output:

csharp
The process cannot access the file because it is being used by another process.
cmd
rem Correct: step out first
cd C:\Projects
rmdir /S /Q myapp

Output: (none — exits 0 on success)

Handling read-only files in the tree

rmdir /S fails on directories containing read-only or system files. Clear the attributes first with attrib, then remove.

cmd
attrib -R -H -S C:\Temp\stubborn\* /S /D
rmdir /S /Q C:\Temp\stubborn

Output: (none — exits 0 on success)

Removing multiple directories

List multiple paths separated by spaces.

cmd
rmdir /S /Q C:\Temp\build C:\Temp\dist C:\Temp\cache

Output: (none — exits 0 on success)

Common pitfalls

  1. Forgetting /Q in scripts — without /Q, rmdir /S pauses for a Y/N prompt that hangs unattended scripts forever.
  2. Cannot remove the current directorycd to the parent before running rmdir on it.
  3. Read-only or system files block removal — run attrib -R -H -S * /S /D in the target tree first.
  4. Symlinks vs real directoriesrmdir on a junction or symlink removes the link only, not the target contents. This is usually the desired behaviour, but verify with dir /AL.
  5. No wildcard supportrmdir /S /Q temp* is not valid; use a for loop to remove multiple matching directories.

Real-world recipes

Remove all __pycache__ directories in a project

cmd
for /d /r C:\Projects\myapp %d in (__pycache__) do @if exist "%d" rmdir /S /Q "%d"

Output: (none — exits 0 on success)

Clean build artefacts before a fresh build

cmd
rmdir /S /Q build dist .mypy_cache

Output: (none — exits 0 on success)

Safely remove a folder only if it exists

cmd
if exist C:\Temp\OldRelease rmdir /S /Q C:\Temp\OldRelease
echo Done.

Output:

code
Done.

Remove a directory tree containing read-only files

cmd
attrib -R -S -H C:\Temp\locked\* /S /D
rmdir /S /Q C:\Temp\locked

Output: (none — exits 0 on success)

Exit codes and error reporting

rmdir returns 0 on success and a non-zero %ERRORLEVEL% on failure, but the same exit code can mean several different things (path not found, directory not empty, access denied, file in use). Inspect both %ERRORLEVEL% and the textual message in scripts that need to recover.

cmd
rmdir C:\Temp\notempty
echo Exit code: %ERRORLEVEL%

Output:

vbnet
The directory is not empty.
Exit code: 145
cmd
rmdir C:\NoSuch\Folder
echo Exit code: %ERRORLEVEL%

Output:

arduino
The system cannot find the file specified.
Exit code: 2
CodeTypical cause
0Removed successfully
2Path or parent does not exist
5Access denied (insufficient ACL)
32File is in use by another process
145Directory not empty (no /S)

rmdir on a reparse point removes the link itself, not the target it points to. This is almost always what you want — but if the link is a directory symbolic link created with mklink /D, attempting rmdir /S on it will follow the link and start recursively deleting the target. Use dir /AL to identify reparse points before deleting.

cmd
rem List reparse points in a directory
dir /AL C:\Data

Output:

ini
2026-04-28  10:00    <JUNCTION>     link [C:\Data\actual]
2026-04-28  10:00    <SYMLINKD>     quick [\\myhost\share]
cmd
rem Safe: remove the junction without touching target
rmdir C:\Data\link

Output: (none — only the junction entry is removed)

cmd
rem DANGEROUS: rmdir /S on a directory symlink can delete target contents
rem Use without /S to remove only the link
rmdir C:\Quick

Output: (none — only the symlink entry is removed)

Long path support

Like mkdir, rmdir is subject to the historical 260-character MAX_PATH limit. Deeply nested trees created by build tools, npm, or container layers frequently exceed this and refuse to be removed with rmdir /S. Workarounds: enable LongPathsEnabled, prefix the target with \\?\, or use robocopy to mirror an empty directory over the offending tree.

cmd
rem Robocopy trick: mirror an empty directory over a too-deep tree
mkdir C:\Temp\empty
robocopy C:\Temp\empty C:\path\that\is\too\deep /MIR /R:0 /W:0 /NFL /NDL /NJH /NJS
rmdir C:\Temp\empty
rmdir C:\path\that\is\too\deep

Output:

markdown
                Total    Copied   Skipped  Mismatch    FAILED    Extras
    Dirs :        50         1         0         0         0        49

PowerShell equivalents

PowerShell's Remove-Item (aliased rmdir, rd, rm, del, erase, ri) is far more capable than the cmd.exe builtin. It accepts pipeline input, supports -Recurse, -Force, -WhatIf, -Confirm, and -Include/-Exclude glob filters. Note that PowerShell's aliases can be confusing — rmdir in PowerShell is not the cmd.exe rmdir; it is Remove-Item with PowerShell semantics. In particular, Remove-Item by default refuses to recurse without -Recurse, even when the target is obviously a directory.

powershell
# Remove an empty directory
Remove-Item C:\Temp\emptydir

Output: (none — silent success)

powershell
# Recursive remove — equivalent of rmdir /S /Q
Remove-Item C:\Temp\OldBuild -Recurse -Force

Output: (none — silent success)

powershell
# Dry run with -WhatIf before committing
Remove-Item C:\Temp\OldBuild -Recurse -Force -WhatIf

Output:

csharp
What if: Performing the operation "Remove Directory" on target "C:\Temp\OldBuild".
What if: Performing the operation "Remove File" on target "C:\Temp\OldBuild\main.py".
...
powershell
# Interactive confirmation for each item
Remove-Item C:\Temp\OldBuild -Recurse -Confirm

Output:

less
Confirm
Are you sure you want to perform this action?
Performing the operation "Remove Directory" on target "C:\Temp\OldBuild".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
powershell
# Pipeline: remove all __pycache__ directories under a tree
Get-ChildItem C:\Projects\myapp -Recurse -Directory -Filter __pycache__ |
    Remove-Item -Recurse -Force

Output: (none — silent success)

powershell
# Pattern-based recursive purge with -Include and -Recurse
Remove-Item C:\Projects\myapp\* -Include '*.pyc','*.pyo' -Recurse -Force

Output: (none — silent success)

powershell
# Robust delete with error handling and read-only attribute stripping
Get-ChildItem C:\Temp\locked -Recurse -Force |
    ForEach-Object { $_.Attributes = 'Normal' }
Remove-Item C:\Temp\locked -Recurse -Force

Output: (none — silent success)

CMD vs PowerShell vs bash comparison

GoalCMDPowerShellbash (Linux/macOS)
Remove empty dirrmdir fooRemove-Item foormdir foo
Recursive force deletermdir /S /Q fooRemove-Item foo -Recurse -Forcerm -rf foo
Interactive confirmrmdir /S foo (single prompt)Remove-Item foo -Recurse -Confirm (per item)rm -ri foo (per item)
Dry run / preview(none built in)Remove-Item foo -Recurse -WhatIf(none; use echo rm -rf foo or find ... -print)
Skip if missingif exist foo rmdir /S /Q fooRemove-Item foo -ErrorAction SilentlyContinuerm -rf foo (no error)
Strip read-only firstattrib -R -S -H foo\* /S /D then rmdir /S /Q fooGet-ChildItem foo -Recurse | % { $_.Attributes='Normal' } then Remove-Item foo -Recurse -Forcechmod -R u+w foo then rm -rf foo
Remove only link, not targetrmdir junctionname (no /S)Remove-Item junctionnamerm linkname (never recurses through link)
Remove all matching by patternfor /d %%d in (build*) do rmdir /S /Q "%%d"Remove-Item build* -Recurse -Forcerm -rf build*

Common pitfalls (continued)

  1. rmdir /S recurses into junctions and symlinks pointing to directories — verify with dir /AL first. Removing the link entry (no /S) is almost always safer.
  2. Files held open by background services — even after closing the app, OneDrive, Search Indexer, or antivirus may hold handles. Reboot or use handle.exe from Sysinternals to identify the locker.
  3. Folder names with leading whitespacecmd.exe parsing drops leading spaces from arguments; quote the entire path or use the \\?\ prefix.
  4. rmdir deletes silently with /Q — no log of what was removed. For audited deletes, prefer robocopy /MIR from an empty source so the log shows every deleted file.
  5. del * does not empty the directory enoughdel leaves subdirectories behind. To truly empty a folder, run del /Q /F /S * then for /d %%d in (*) do rmdir /S /Q "%%d".

Real-world recipes (continued)

Empty a folder without removing the folder itself

cmd
del /Q /F /S "C:\Temp\Scratch\*" >NUL 2>&1
for /d %%d in ("C:\Temp\Scratch\*") do rmdir /S /Q "%%d"

Output: (none — exits 0 on success)

Conditional purge with logging

cmd
@echo off
setlocal EnableExtensions
set TARGET=C:\Temp\build
set LOG=C:\Logs\purge_%DATE:~-4,4%%DATE:~-10,2%%DATE:~-7,2%.log
if exist "%TARGET%" (
    dir /S /B "%TARGET%" > "%LOG%"
    rmdir /S /Q "%TARGET%" && echo Purged %TARGET% >> "%LOG%"
) else (
    echo %TARGET% not present >> "%LOG%"
)
endlocal

Output: (none — log file written)

Remove all node_modules folders in a workspace

cmd
for /d /r C:\Code %%d in (node_modules) do @if exist "%%d" rmdir /S /Q "%%d"

Output: (none — exits 0 on success)

Mirror-delete with robocopy when rmdir chokes on long paths

cmd
mkdir C:\Temp\empty
robocopy C:\Temp\empty C:\Projects\too\deep\to\rmdir /MIR /R:1 /W:1 /NFL /NDL /NJH /NJS
rmdir C:\Temp\empty
rmdir C:\Projects\too\deep\to\rmdir

Output:

yaml
   Files :       300         0         0         0         0       300

PowerShell scheduled cleanup of old log folders

powershell
$cutoff = (Get-Date).AddDays(-30)
Get-ChildItem C:\Logs -Directory |
    Where-Object { $_.LastWriteTime -lt $cutoff } |
    Remove-Item -Recurse -Force -Confirm:$false

Output: (none — silent success)

See also

  • mkdir — the inverse: create directories.
  • del — remove files only, leaving directory structure intact.
  • attrib — clear read-only/hidden/system flags before rmdir /S on stubborn trees.
  • robocopy /MIR — alternative deletion strategy that handles long paths.
  • takeown and icacls — claim ownership and grant rights before removing protected directories.
  • Linux rmdir / rm -rf — closest equivalents on Unix.

Sources