03 Windows 与 Linux 路径差异

1. 基本差异

项目 Windows Linux/POSIX
C:\、UNC /
常见分隔符 \ /
大小写 通常不敏感 通常敏感
可执行权限 主要由扩展名/系统规则 execute bit
占用文件删除 经常失败 可删除目录项,句柄仍可读

不要在源码中写死分隔符:

const target = `${root}\\docs\\index.md`;

应使用:

const target = path.join(root, "docs", "index.md");

2. path.posixpath.win32

默认 path 根据当前操作系统解释字符串。即使程序运行在 Windows,ZIP Entry 仍按照 ZIP 规范使用 /

docs/guide/index.md

因此解析 ZIP 内部名称时应使用:

const normalizedEntry = path.posix.normalize(entry.fileName);

验证通过后,再按 / 分段并交给本地 path.join()

const segments = normalizedEntry.split("/");
const localPath = path.join(extractRoot, ...segments);

3. Windows 特殊情况

除了盘符,还需注意:

\\server\share\file.txt   UNC
\\?\C:\very-long-path    扩展长度路径
CON、NUL、AUX             保留名称
name.、name␠              末尾点号或空格

不要试图仅靠一个“删除特殊字符”的正则解决所有问题。物理存储名宜使用 UUID/Hash,原始名称只作为元数据;解压时对每一个路径段执行明确规则。

4. 大小写冲突

ZIP 中可能同时出现:

docs/Readme.md
docs/README.md

在 Linux 上可以是两个文件,在常见 Windows 文件系统上会冲突。跨平台服务应定义策略:拒绝大小写折叠后重复的路径,或者部署时固定只在一种大小写语义的文件系统处理。

5. ZIP 路径验证顺序

读取原始 entry.fileName
  → 拒绝 NUL 字符
  → 把反斜杠视作可疑分隔符或统一拒绝
  → 使用 path.posix 解析
  → 拒绝绝对路径
  → 拒绝空段、.、..
  → 校验每个路径段
  → 检查大小写折叠冲突
  → 转换成本地目标路径
  → 再执行 root 边界检查

不要先调用只处理文件名的函数,它会丢失目录信息和安全上下文。

6. 环境变量中的多路径

PATH 等变量的分隔符不同:

const directories = process.env.PATH?.split(path.delimiter);

Windows 通常是 ;,POSIX 通常是 :

练习题

  1. 使用 path.win32path.posix 分别解析同一个字符串。
  2. 设计 ZIP 中 README.mdreadme.md 的冲突策略。
  3. 解释 ZIP Entry 为什么不应直接用当前系统的 path.normalize()
  4. 列出五个在 Windows 成功、到 Linux 失败的路径假设。