Windows 保留设备名(nul / con / aux)文件无法删除
一句话总结:
nul、con、aux这类文件名被 Windows 当作设备名而非普通文件,资源管理器与常规del命令在 Win32 路径解析层就被拦截,所以表现为「文件明明存在却删不掉」;解法是绕过路径解析——加\\?\扩展前缀,或换用 Git Bash / WSL 直接走底层文件系统。来源:DeepSeek 对话《文件无法删除》 https://chat.deepseek.com/share/nv1d8fd46lceyiv0jn
1. 为什么删不掉
Windows 继承自 MS-DOS,把一批名字保留给硬件设备,任何同名的普通文件都会被系统当作设备来处理:
| 保留名 | 原本含义 |
|---|---|
NUL |
空设备(写入即丢弃、读取即 EOF) |
CON |
控制台 |
PRN |
打印机 |
AUX |
辅助设备(通常是串口) |
COM1 ~ COM9 |
串口 |
LPT1 ~ LPT9 |
并口 |
Win32 子系统在解析路径时先做保留名匹配,一旦命中就直接转发给设备驱动,根本不会走到文件系统。所以资源管理器觉得「该路径不存在」,删除操作自然失败;com1.txt 这类「设备名 + 扩展名」的形式同样会被拦截。
这些是行业共识的客观事实,与是否管理员权限无关——提权也删不掉。
2. 核心思路:\\?\ 扩展路径前缀
\\?\ 是 Win32 的 UNC 扩展路径格式,启用后会关闭路径规范化(不再解析 ..、/、保留设备名),路径被原样交给内核对象管理器,直接落到 NTFS 上。
- 只支持绝对路径,且必须用反斜杠:
\\?\C:\dir\nul有效,\\?\C:/dir/nul无效 - 注意转义:CMD / PowerShell 里是两个反斜杠
\\?\,在 JSON、正则或部分脚本语言中需要写成四个\\\\?\\
3. 可选的删除方案
| 方案 | 核心命令 | 优势 | 前提 |
|---|---|---|---|
| .NET API | [System.IO.File]::Delete('\\?\C:\完整路径\nul') |
最直接,一次调用 | 有 PowerShell |
| PowerShell | Remove-Item -LiteralPath "\\?\C:\完整路径\nul" -Force |
无需记 .NET 语法 | 有 PowerShell |
| CMD | del "\\?\C:\完整路径\nul" |
最短 | 命令提示符 |
| Git Bash | 在文件所在目录右键 → Git Bash Here → rm nul |
最简单,容错高 | 已装 Git for Windows |
| WSL | rm /mnt/c/完整路径/nul |
走 Linux 内核,彻底规避 Win32 限制 | 已装 WSL |
| 7-Zip | 用 7-Zip 打开父目录,选中后 Shift + Delete |
图形界面,兜底手段 | 资源管理器已失效时 |
4. 操作步骤
4.1 先拿到绝对路径
在资源管理器中选中该文件,按住 Shift 右键 → 「复制为路径」,得到带引号的完整路径。后续命令把引号去掉、在前面补上 \\?\ 即可。
4.2 PowerShell
或直接用 .NET:
-LiteralPath 不能省:-Path 仍会做通配符与路径解析,可能再次踩到保留名。
4.3 CMD
4.4 Git Bash(推荐图省事时用)
- 打开文件所在的文件夹
- 在空白处右键 → Git Bash Here
- 执行:
Git Bash 自带 MSYS2 运行时,会用 POSIX 路径语义直接调用底层接口,nul 就是普通文件名,没有特殊性。
4.5 WSL
4.6 7-Zip
用 7-Zip 的文件管理器打开父目录,找到 nul 后按 Shift + Delete。7-Zip 不走资源管理器的路径校验,部分场景可以直接删掉;删不掉时还能先重命名再删。
5. 排查顺序建议
- Git Bash 里
rm nul—— 一步到位,几乎必成 - 没装 Git → PowerShell 的
\\\\?\\方案 - 还失败 → 7-Zip 重命名后再删
- 仍然报错时,记下具体错误码(如
Access denied/The system cannot find the file specified),再判断是权限、文件系统损坏还是被进程占用
6. 预防
- 从 Linux / WSL / 压缩包解压、跨平台同步时最容易带出这类文件,解压到 NTFS 前先看文件名
- 建目录时避免
con、prn、aux、nul、com1~com9、lpt1~lpt9,含扩展名也算 - 写脚本批量生成文件名时,把保留名列表做成黑名单过滤