Node.js:path(路径)
path 模块用来处理文件和目录路径。引入方式:require('path') 或 require('node:path'),二者等价。它做的是 字符串层面 的拼接、解析、规范化,不会访问磁盘。
默认行为跟当前操作系统走:在 Windows 上按 Windows 路径规则处理,在 POSIX 上按 POSIX 规则处理。跨平台要结果一致时,显式用 path.win32 或 path.posix。
Windows 与 POSIX 的对比
Windows 风格路径
标准 DOS 路径可由以下三部分组成:
- 卷号或驱动器号,后跟卷分隔符(
:) - 目录名称。目录分隔符用来分隔嵌套目录
- 可选的文件名。目录分隔符用来分开路径和文件名
三项都有,就是绝对路径。未指定卷号、且目录以分隔符开头,则相对 当前驱动器的根。否则相对当前目录。常见写法:
| 路径 | 描述 |
|---|---|
C:\Documents\Newsletters\Summer2018.pdf |
C: 驱动器上的绝对文件路径 |
\Program Files\Custom Utilities\StringFinder.exe |
当前驱动器根上的绝对路径 |
2018\January.xlsx |
当前目录的子目录中的相对路径 |
..\Publications\TravelBrochure.pdf |
从当前目录上溯后再进入的相对路径 |
C:\Projects\apilibrary\apilibrary.sln |
C: 驱动器上的绝对路径 |
C:Projects\apilibrary\apilibrary.sln |
相对 C: 驱动器 当前目录 的路径(冒号后没有分隔符) |
Windows 上 Node 还遵循「每个驱动器各自一份当前目录」:path.resolve('C:\\') 和 path.resolve('C:') 结果可以不同。
POSIX 是什么
POSIX(Portable Operating System Interface,可移植操作系统接口)是 IEEE(Institute of Electrical and Electronics Engineers,电气与电子工程师协会)为各种 UNIX 类系统上的应用程序定义的一套 API 标准,正式称呼是 IEEE 1003,国际标准编号 ISO/IEC 9945。制定它是为了让同一套程序更容易在不同 UNIX 之间移植。
POSIX.1 已被 ISO(International Organization for Standardization,国际标准化组织)接受,对应 ISO/IEC 9945-1:1990。
POSIX 主要由四部分组成:
- XBD(Base Definitions volume):通用术语、概念、接口,以及
cd、mkdir、cp、mv等工具和stdio.h、stdlib.h、pthread.h等头文件 - XSH(System Interface volume):系统服务,例如线程、套接字、标准 IO、信号处理、错误处理
- XCU(Shell and Utilities volume):shell 语法、关键字,以及
break、cd、cp、continue、pwd、return等 - XRAT(Rationale volume):标准相关的历史,以及采纳或舍弃某功能的理由
在 Node 的 path 文档里,POSIX 就是「非 Windows 的那套路径规则」:分隔符是 /,没有盘符。
不同的路径解析结果
path.basename() 在 POSIX 和 Windows 上结果不同。Windows 同时接受正斜杠 / 和反斜杠 \ 做目录分隔符;Unix 类系统只认 /。
在 Windows 上:
const path = require('path');
path.basename('C:\\a\\b.html'); // 'b.html'
path.win32.basename('C:\\a\\b.html'); // 'b.html'
要在任意操作系统上按 Windows 规则解析 Windows 路径,用 path.win32。
在 POSIX 上:
const path = require('path');
path.basename('C:\\a\\b.html'); // 'C:\\a\\b.html'
path.posix.basename('C:\\a\\b.html'); // 'C:\\a\\b.html'
path.win32.basename('C:\\a\\b.html'); // 'b.html'
要在任意操作系统上按 POSIX 规则解析 POSIX 路径,用 path.posix。
path 方法
basename(path[, ext])
返回路径的最后一部分,尾部分隔符会被忽略。第二个参数是可选的扩展名;匹配时会从结果里去掉这段后缀。
path 不是字符串,或给了 ext 但不是字符串,抛 TypeError。
const path = require('path');
path.basename('/demo-lizh/node/package'); // 'package'
path.basename('/demo-lizh/node/package.json'); // 'package.json'
path.basename('/demo-lizh/node/package.json', '.json'); // 'package'
Windows 通常不区分文件名大小写,但 basename 对扩展名 区分大小写:
path.basename('/demo-lizh/node/package.json', '.json'); // 'package'
path.basename('/demo-lizh/node/package.json', '.JSON'); // 'package.json'
dirname(path)
返回路径的目录名,尾部分隔符会被忽略。path 不是字符串则抛 TypeError。
const path = require('path');
path.dirname('/demo-lizh/node/package'); // '/demo-lizh/node'
path.dirname('/demo-lizh/node/package/'); // '/demo-lizh/node'
path.dirname('/demo-lizh/node/package.json'); // '/demo-lizh/node'
extname(path)
返回扩展名:最后一部分里 最后一个 . 起到字符串末尾。最后一部分没有 .,或除了第一个字符外没有 .(例如 .package),返回空字符串。path 不是字符串则抛 TypeError。
const path = require('path');
path.extname('/demo-lizh/node/package'); // ''
path.extname('.package'); // ''
path.extname('/demo-lizh/node/package.json'); // '.json'
path.extname('index.coffee.md'); // '.md'
path.extname('index.'); // '.'
normalize(path)
规范化路径:解析 .. 和 .;连续多个分隔符(POSIX 上的 /,Windows 上的 \ 或 /)收成一个平台分隔符。尾部分隔符会保留。空字符串返回 '.'(当前工作目录)。不是字符串则抛 TypeError。
这是字符串规范化,并不严格等同于操作系统解析路径的全部规则(例如 POSIX 上两个前导 / 有时有特殊含义,这里仍会收成一个 /)。
在 POSIX 上:
const path = require('path');
path.normalize('demo-lizh/node////////package.json'); // 'demo-lizh/node/package.json'
path.normalize('demo-lizh/node/package.json/..'); // 'demo-lizh/node'
path.normalize(''); // '.'
在 Windows 上:
path.normalize('C:\\temp\\\\foo\\bar\\..\\'); // 'C:\\temp\\foo\\'
path.win32.normalize('C:////temp\\\\/\\/\\/foo/bar'); // 'C:\\temp\\foo\\bar'
join([...paths])
用平台分隔符把片段接起来,再做一次 normalize。长度为 0 的片段会忽略;接完后仍是空字符串则返回 '.'。任一片段不是字符串则抛 TypeError。
const path = require('path');
path.join('demo-lizh', 'node', 'package.json'); // POSIX: 'demo-lizh/node/package.json'
path.join('demo-lizh', '', 'package.json'); // 'demo-lizh/package.json'
path.join(''); // '.'
resolve([...paths])
把一段路径解析成 绝对路径。从右往左处理,后面的片段接到前面,直到构造出绝对路径。
- 全部处理完仍不是绝对路径,就用当前工作目录补上
- 结果会规范化,尾部斜杠会去掉(解析到根目录除外)
- 零长度片段忽略
- 一个参数都不传,返回当前工作目录的绝对路径
- 任一参数不是字符串则抛
TypeError
const path = require('path');
path.resolve('/demo-lizh', 'node', 'package.json'); // '/demo-lizh/node/package.json'
path.resolve('demo-lizh', 'node', 'package.json'); // 相对 cwd,例如 '/Users/lizhao/Documents/.../demo-lizh/node/package.json'
path.resolve('/demo-lizh', '/node', 'package.json'); // '/node/package.json'
path.resolve('/demo-lizh/node/json/', '../package.json'); // '/demo-lizh/node/package.json'
path.resolve(''); // 当前工作目录
path.resolve(); // 当前工作目录
relative(from, to)
根据当前工作目录,给出从 from 到 to 的相对路径。两者经 path.resolve() 后相同,则返回空字符串。from 或 to 传空字符串时,用当前工作目录代替。不是字符串则抛 TypeError。
在 POSIX 上:
const path = require('path');
path.relative('/demo-lizh/node/nodejs/path.js', '/demo-lizh/node/package.json'); // '../../package.json'
path.relative('node/package.json', 'node/package.json'); // ''
path.relative('node/package.json', ''); // 相对 cwd,例如 '../..'
在 Windows 上:
path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb'); // '..\\..\\impl\\bbb'
UNC 路径在 Windows 上会保留前导斜杠。
format(pathObject)
把对象拼成路径字符串,是 path.parse() 的逆操作。对象字段:
{
dir: '',
root: '',
base: '',
name: '',
ext: ''
}
优先级:
- 提供了
dir,则忽略root - 提供了
base,则忽略ext和name
const path = require('path');
path.format({
root: '/Documents/',
name: 'package-lock',
ext: '.json'
});
// '/Documents/package-lock.json'
path.format({
dir: '/lizhao/demo-lizh/node',
base: 'package.json',
root: '/Documents',
name: 'package-lock',
ext: '.json'
});
// '/lizhao/demo-lizh/node/package.json'
只有 root、没有 dir 时,root 按根路径来写(POSIX 上常带末尾 /);有 dir 时不必再写 root。Node 19 起,ext 没写点号也会自动补上:ext: 'json' 与 ext: '.json' 效果相同。
parse(path)
把路径拆成对象,尾部分隔符忽略。path 不是字符串则抛 TypeError。字段是 root、dir、base、ext、name。
在 POSIX 上:
const path = require('path');
console.log(path.parse('/demo-lizh/node/package.json'));
// {
// root: '/',
// dir: '/demo-lizh/node',
// base: 'package.json',
// ext: '.json',
// name: 'package'
// }
console.log(path.parse('demo-lizh/node/package.json'));
// { root: '', dir: 'demo-lizh/node', base: 'package.json', ext: '.json', name: 'package' }
console.log(path.parse(''));
// { root: '', dir: '', base: '', ext: '', name: '' }
绝对路径的 dir 包含 root(/ 或 C:\\),相对路径的 root 是空字符串。
在 Windows 上:
const path = require('path');
path.parse('C:\\demo-lizh\\node\\package.json');
// {
// root: 'C:\\',
// dir: 'C:\\demo-lizh\\node',
// base: 'package.json',
// ext: '.json',
// name: 'package'
// }
path.parse('demo-lizh\\node\\package.json');
// { root: '', dir: 'demo-lizh\\node', base: 'package.json', ext: '.json', name: 'package' }
path.parse('');
// { root: '', dir: '', base: '', ext: '', name: '' }
isAbsolute(path)
判断 字面量 路径是不是绝对路径。空字符串返回 false。不是字符串则抛 TypeError。它不做磁盘解析,也 不能 用来防路径穿越。
在 POSIX 上:
const path = require('path');
path.isAbsolute('/demo-lizh/node/package.json'); // true
path.isAbsolute('./package.json'); // false
path.isAbsolute(''); // false
在 Windows 上:
path.isAbsolute('//server'); // true
path.isAbsolute('\\\\server'); // true
path.isAbsolute('C:/foo/..'); // true
path.isAbsolute('C:\\foo\\..'); // true
path.isAbsolute('bar\\baz'); // false
path.isAbsolute('bar/baz'); // false
path.isAbsolute('.'); // false
toNamespacedPath(path)
仅 Windows 有意义:返回带命名空间前缀的等价路径(例如 \\?\ 或 UNC 的 \\?\UNC\),以便绕过旧版 MAX_PATH 限制。path 不是字符串则原样返回。POSIX 上这是空操作,始终返回入参。
matchesGlob(path, pattern)
判断 path 是否匹配 glob 模式。path 或 pattern 不是字符串则抛 TypeError。Node 22.5、20.17 起提供,后续版本已标为稳定。
path.matchesGlob('/foo/bar', '/foo/*'); // true
path.matchesGlob('/foo/bar*', 'foo/bird'); // false
path 属性
posix
POSIX 实现。也可用 require('path/posix') 或 require('node:path/posix') 直接拿到。
path.posix.basename('/demo-lizh/node/package');
path.posix.join('demo-lizh', 'node', 'package.json');
win32
Windows 实现。也可用 require('path/win32') 或 require('node:path/win32')。
path.win32.basename('/demo-lizh/node/package');
path.win32.join('demo-lizh', 'node', 'package.json');
Windows 上 path 方法写入路径时只用 \,但解析时 / 和 \ 都认。
delimiter
平台路径 列表 定界符:Windows 是 ;,POSIX 是 :。用来拆 PATH 这类环境变量,不是文件路径里的 /、\。
在 POSIX 上:
const path = require('path');
process.env.PATH; // /usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin
process.env.PATH.split(path.delimiter); // ['/usr/local/bin', '/usr/local/sbin', '/usr/bin', '/bin', '/usr/sbin', '/sbin']
在 Windows 上:
process.env.PATH; // C:\Windows\system32;C:\Windows;C:\Program Files\node\
process.env.PATH.split(path.delimiter); // ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']
sep
平台路径 片段 分隔符:Windows 是 \,POSIX 是 /。
在 POSIX 上:
path.sep; // '/'
'foo/bar/baz'.split(path.sep); // ['foo', 'bar', 'baz']
在 Windows 上:
path.sep; // '\\'
'foo\\bar\\baz'.split(path.sep); // ['foo', 'bar', 'baz']