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).
rmdir /?
Output:
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.
rmdir [/S] [/Q] path
rd [/S] [/Q] path
Output: (none on success; error if directory not empty or not found)
Essential options
| Switch | Meaning |
|---|---|
/S | Remove the directory tree including all files and subdirectories |
/Q | Quiet mode — no confirmation prompt when using /S |
Removing an empty directory
Without /S, rmdir only succeeds if the directory is already empty.
rmdir C:\Temp\emptydir
Output: (none — exits 0 on success)
rem rd is the same command
rd C:\Temp\emptydir
Output: (none — exits 0 on success)
rem Fails if directory is not empty
rmdir C:\Projects\myapp
Output:
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.
rem Interactive — prompts once
rmdir /S C:\Temp\OldBuild
Output:
C:\Temp\OldBuild, Are you sure (Y/N)? Y
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.
cd C:\Projects\myapp
rmdir /S /Q C:\Projects\myapp
Output:
The process cannot access the file because it is being used by another process.
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.
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.
rmdir /S /Q C:\Temp\build C:\Temp\dist C:\Temp\cache
Output: (none — exits 0 on success)
Common pitfalls
- Forgetting
/Qin scripts — without/Q,rmdir /Spauses for aY/Nprompt that hangs unattended scripts forever. - Cannot remove the current directory —
cdto the parent before runningrmdiron it. - Read-only or system files block removal — run
attrib -R -H -S * /S /Din the target tree first. - Symlinks vs real directories —
rmdiron a junction or symlink removes the link only, not the target contents. This is usually the desired behaviour, but verify withdir /AL. - No wildcard support —
rmdir /S /Q temp*is not valid; use aforloop to remove multiple matching directories.
Real-world recipes
Remove all __pycache__ directories in a project
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
rmdir /S /Q build dist .mypy_cache
Output: (none — exits 0 on success)
Safely remove a folder only if it exists
if exist C:\Temp\OldRelease rmdir /S /Q C:\Temp\OldRelease
echo Done.
Output:
Done.
Remove a directory tree containing read-only files
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.
rmdir C:\Temp\notempty
echo Exit code: %ERRORLEVEL%
Output:
The directory is not empty.
Exit code: 145
rmdir C:\NoSuch\Folder
echo Exit code: %ERRORLEVEL%
Output:
The system cannot find the file specified.
Exit code: 2
| Code | Typical cause |
|---|---|
| 0 | Removed successfully |
| 2 | Path or parent does not exist |
| 5 | Access denied (insufficient ACL) |
| 32 | File is in use by another process |
| 145 | Directory not empty (no /S) |
Junctions, symbolic links, and reparse points
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.
rem List reparse points in a directory
dir /AL C:\Data
Output:
2026-04-28 10:00 <JUNCTION> link [C:\Data\actual]
2026-04-28 10:00 <SYMLINKD> quick [\\myhost\share]
rem Safe: remove the junction without touching target
rmdir C:\Data\link
Output: (none — only the junction entry is removed)
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.
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:
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.
# Remove an empty directory
Remove-Item C:\Temp\emptydir
Output: (none — silent success)
# Recursive remove — equivalent of rmdir /S /Q
Remove-Item C:\Temp\OldBuild -Recurse -Force
Output: (none — silent success)
# Dry run with -WhatIf before committing
Remove-Item C:\Temp\OldBuild -Recurse -Force -WhatIf
Output:
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".
...
# Interactive confirmation for each item
Remove-Item C:\Temp\OldBuild -Recurse -Confirm
Output:
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"):
# 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)
# Pattern-based recursive purge with -Include and -Recurse
Remove-Item C:\Projects\myapp\* -Include '*.pyc','*.pyo' -Recurse -Force
Output: (none — silent success)
# 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
| Goal | CMD | PowerShell | bash (Linux/macOS) |
|---|---|---|---|
| Remove empty dir | rmdir foo | Remove-Item foo | rmdir foo |
| Recursive force delete | rmdir /S /Q foo | Remove-Item foo -Recurse -Force | rm -rf foo |
| Interactive confirm | rmdir /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 missing | if exist foo rmdir /S /Q foo | Remove-Item foo -ErrorAction SilentlyContinue | rm -rf foo (no error) |
| Strip read-only first | attrib -R -S -H foo\* /S /D then rmdir /S /Q foo | Get-ChildItem foo -Recurse | % { $_.Attributes='Normal' } then Remove-Item foo -Recurse -Force | chmod -R u+w foo then rm -rf foo |
| Remove only link, not target | rmdir junctionname (no /S) | Remove-Item junctionname | rm linkname (never recurses through link) |
| Remove all matching by pattern | for /d %%d in (build*) do rmdir /S /Q "%%d" | Remove-Item build* -Recurse -Force | rm -rf build* |
Common pitfalls (continued)
rmdir /Srecurses into junctions and symlinks pointing to directories — verify withdir /ALfirst. Removing the link entry (no/S) is almost always safer.- Files held open by background services — even after closing the app, OneDrive, Search Indexer, or antivirus may hold handles. Reboot or use
handle.exefrom Sysinternals to identify the locker. - Folder names with leading whitespace —
cmd.exeparsing drops leading spaces from arguments; quote the entire path or use the\\?\prefix. rmdirdeletes silently with/Q— no log of what was removed. For audited deletes, preferrobocopy /MIRfrom an empty source so the log shows every deleted file.del *does not empty the directory enough —delleaves subdirectories behind. To truly empty a folder, rundel /Q /F /S *thenfor /d %%d in (*) do rmdir /S /Q "%%d".
Real-world recipes (continued)
Empty a folder without removing the folder itself
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
@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
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
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:
Files : 300 0 0 0 0 300
PowerShell scheduled cleanup of old log folders
$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 beforermdir /Son stubborn trees.robocopy /MIR— alternative deletion strategy that handles long paths.takeownandicacls— claim ownership and grant rights before removing protected directories.- Linux
rmdir/rm -rf— closest equivalents on Unix.
Sources
- rmdir | Microsoft Learn — official Windows Server command reference
- rd | Microsoft Learn —
rdalias reference (identical semantics) - windowsserverdocs/rmdir.md on GitHub — current source of truth for the docs page