Python pathlib 拼接路径后父目录消失
pathlib 遇到后续绝对路径分量会舍弃前面的目录。区分 POSIX 绝对路径、Windows 根路径与相对路径,修正输入约定,避免用删除斜杠代替安全验证。
用 Path(base, child) 拼接目录时,结果中只剩下 child,原来的父目录消失。例如在 POSIX 路径规则下:
from pathlib import PurePosixPath
base = PurePosixPath('/srv/reports')
child = '/daily.csv'
print(base / child) # /daily.csv
这不是分隔符被漏加了。后一个分量以 / 开头,是绝对路径;pathlib 按路径语义处理它,前面的目录会被舍弃。改成 Path(base, child) 或 base.joinpath(child) 不会改变这条规则。
这里讨论 Python 3 的 pathlib 路径构造。PurePosixPath 和 PureWindowsPath 便于在任意系统上检查对应路径规则;实际使用的 Path 会采用当前系统的路径类型。
检查传入值,而不只看拼接表达式
先查看父目录和子路径的实际字符串,尤其注意接口返回值是否自带根目录前缀:
from pathlib import PurePosixPath
base = PurePosixPath('/srv/reports')
child_text = '/daily.csv'
child = PurePosixPath(child_text)
print(repr(child_text))
print(child.anchor)
print(child.is_absolute())
print(base / child)
这个例子中,anchor 是 /,is_absolute() 为真。调用方以为 child_text 是“父目录下的文件名”,输入本身却表达了“文件系统根目录下的文件”。两种含义需要在数据交接处明确。
如果输入应当是相对父目录的路径,就把它提供为相对路径:
from pathlib import Path
base = Path('/srv/reports')
child_text = 'daily/summary.csv'
target = base / child_text
print(target)
在 POSIX 系统上,目标是 /srv/reports/daily/summary.csv。以上代码只构造路径,不创建目录,也不打开文件。调用真实写入逻辑之前,先核对 target 是否符合接口约定。
Windows 的根路径与绝对路径不能混为一谈
Windows 输入还需要看盘符。下面这些结果可以用 PureWindowsPath 单独检查:
from pathlib import PureWindowsPath
base = PureWindowsPath(r'C:\reports')
print(base / r'\daily.csv') # C:\daily.csv
print(base / r'D:\daily.csv') # D:\daily.csv
print(base / r'daily.csv') # C:\reports\daily.csv
第一种输入从当前盘的根目录开始,保留 C:,但舍弃了 reports。第二种输入同时指定盘符和根目录,会改到 D:。第三种没有根前缀,才会接在父目录之后。
因此,不能仅用 is_absolute() 判断一个 Windows 分量会不会清掉父目录。PureWindowsPath(r'\daily.csv').is_absolute() 为假,因为它没有盘符,但拼接后仍会从父目录所在盘的根目录开始。诊断这类输入时,应同时查看 drive、root 和 anchor。
不要把删除开头斜杠当成通用修复
如果上游协议用 /daily.csv 表示某个虚拟目录内的路径,而不是本机绝对路径,需要先按该协议解释,再转换成本机相对路径。优先修正上游输出或定义清楚的转换函数,并为协议允许的输入写出预期结果。
直接对所有输入调用 lstrip('/') 会悄悄改变路径含义,也处理不了 Windows 反斜杠、盘符和网络共享路径。对于本来就允许用户提供绝对路径的接口,去掉根前缀反而会写到错误位置。转换策略必须服从输入约定,不能仅以“拼接后出现了父目录”为成功标准。
这也不是安全路径验证方案。相对路径仍可能含 ..,目录还可能存在符号链接;pathlib 的普通拼接不会保证最终文件位于指定目录内。若输入来自不可信来源,需要另行设计文件访问边界,不应把删除斜杠或检查字符串前缀当作目录隔离。
修正后至少核对普通文件名、含子目录的相对路径,以及协议是否接受绝对路径和 Windows 根路径。先只打印结果,确认已有调用方没有依赖旧语义,再接回实际文件操作。如果错误路径已经被用于写入,单改拼接不会搬回文件;应先核对两个位置的文件与备份,避免直接覆盖或删除。


