有位朋友让我看他 NAS 上的相册文件夹,说在他那台 Windows 上一点开就卡住,右键删除没反应,复制到移动硬盘还弹「找不到路径」。我以为是共享权限,进去翻了翻,权限没问题,是几个名字不正常。
这种事不是第一次碰上。我干脆把常见的几种都造出来试了一遍,边界在哪儿、哪种删不掉、怎么清,都记在下面。
先记住 259 这个数
Windows 那条老规矩是 260 个字符,这个数里含了结尾的空字符,所以真正能用的只有 259。我造了一组文件,完整路径从 247 字符一路排到 269,用普通的复制接口往共享目录里送:
| 完整路径长度 | 复制到共享 |
|---|---|
| 247 | 成功 |
| 255 | 成功 |
| 257 | 成功 |
| 258 | 成功 |
| 259 | 成功 |
| 260 | 失败 |
| 263 | 失败 |
| 269 | 失败 |
259 和 260 之间就是那道墙,一格都不让。失败时报的话是「找不到路径,因为该路径不存在」——文件就在那儿,隔壁窗口还看得见。
robocopy 的逻辑不一样。同一批目录我用 /E 推过去,它报的是目录 36 个、复制 35 个、跳过 1 个,文件 1 个、复制 1 个,退出码 1。看着挺成功,但那 1 个文件送过去之后,我用普通命令去目标目录数,数出来是 0。它能走长路径,资源管理器不能,于是东西到了,你却看不见,也就删不掉、备份不到。
很多人 NAS 容量一直涨、列表里却找不到东西,多半就是这么堆出来的。
结尾带空格和点的文件,会把你的删除操作骗过去
我造了两个:一个叫 endspace,1 字节;一个叫 endspace 后面跟一个空格,3 字节。资源管理器里两个都显示成 endspace,肉眼分不出来。
然后我删那个带空格的。结果被删掉的是 1 字节那个,带空格的还在。
原因是普通接口拿到路径会先把结尾的空格和点吃掉,它实际去找的是 endspace,正好命中那个正常的。命令不报错,你以为删了,删的是另一个。这种错最难发现,因为界面上的名字完全一样。
复制也是同样的下场,报「路径不存在」。双击更是打不开。
这种名字多半是 macOS 带过来的,Finder 允许文件名结尾留空格,早期的一些压缩包里也常见。同步过来就成了钉子户。
能删的只有加 \\?\ 前缀,让路径规范化这一步整个跳过:
[System.IO.File]::Delete('\\?\D:\你的目录\endspace ')
前缀里必须是反斜杠,写成 //?/ 是不认的。
CON、PRN、NUL 这几个名字,Windows 当它们是设备
从 DOS 时代留下来的保留名:CON、PRN、AUX、NUL、COM1 到 COM9、LPT1 到 LPT9。这些名字后面接什么扩展名都没用,CON.txt 在系统眼里还是控制台。
我用长路径接口硬写,能造出真的 CON.txt,3 字节,老老实实躺在目录里。用普通接口去读它,报找不到路径。用普通接口去删它——这个最坑——命令返回成功,一个错都不报,然后文件还在。我连着试了 CON.txt、con.md、NUL.txt,三个都这样:删除显示成功,剩余列表里一个没少。
所以别信「删完了」这三个字,删完要去数一遍。
名字里带冒号,内容写进去了但你看不见
这个最阴。我写了个 a:b.txt,3 字节,没报任何错。去目录一看,多出来一个叫 a 的文件,0 字节。
那 3 个字节在 a 的备用数据流里,流名叫 b.txt。用流列表能看见:主数据流 0 字节,b.txt 流 3 字节。资源管理器不显示流,dir 也不列,你看到的永远是一个 0 字节的空文件。
我把它经共享复制到另一台机器上,流还跟着过去了,b.txt 那 3 个字节完整到达。但只要目标是 ext4、Btrfs 这种没有流概念的存储——绝大多数 NAS 就是这个——流没地方放,落地只剩 0 字节的 a,内容就真没了。
冒号在 Linux 和 macOS 上是能用的字符,从那边同步过来很容易带进来。NAS 上看到一个 0 字节、名字还少了一截的文件,八成是这么来的。
顺手排掉的几个误会
中文名不是问题:测试文件.txt,本地和共享都正常。emoji 也不是:带片场符号的那种名字一样能建能读。大小写也没事:建 Readme.txt,用 readme.txt 能读到,落盘保留原样。
单段文件名的上限是 255 字符。我造了 212 到 234 字符的文件名,全部正常;再往上到 255 也还行,超过就挂。挂的时候报的也不是「名字太长」,而是找不到文件,很容易被误判成文件丢了。
这台机器的长路径开关我看了一眼,还是关着的。就算打开,也只对声明了自己支持长路径的程序生效,资源管理器不在里面,该打不开还是打不开。想靠一个开关解决,会失望。
清理顺序别搞反
第一步是找,不是删。资源管理器看不到的部分,用前缀加完整路径去列,或者干脆在 WSL 里 ls -la,Linux 那边没有这些限制,一眼全看见。
第二步才是删。能用前缀就逐个删;数量太多就把整个怪目录 robocopy 到一个空目录,不加 /MIR,然后再删那个空目录。
第三步是从源头堵。同步工具和下载器的命名规则里,把结尾空格、结尾点、冒号、保留名都加进过滤;解压前先看一眼包里有没有这些名字,几百个文件里混一个,就够让整个备份任务天天报错了。
你们 NAS 里有没有那种删不掉、也打不开的文件夹,最后是怎么清掉的?