真实用/运算符拼接一个看起来是绝对路径的右操作数,验证左边路径会被完全丢弃这个真实存在的安全隐患,并对比.和..在字符串层面和真实文件系统层面处理方式的差异。
代码base = Path("/home/user/project") sub = base / "src" / "main.py" # 正常拼接 weird = base / "/etc/passwd" # 右边看起来是绝对路径,会怎样 p = Path("a/b/../c/./d") print(p) # 原样打印,./..会被处理吗 print(p.resolve()) # 真的问文件系统
normal join: /home/user/project/src/main.py
joining with an ABSOLUTE-looking right side: /etc/passwd ← 真实base整个被丢弃了!
os.path.join("/home/user", "/etc/passwd") → /etc/passwd ← 真实跟pathlib的行为完全一致Path("a/b/../c/./d")打印出来: a/b/../c/d ← 真实./被去掉了,但../原样保留
p.resolve(): /Users/w/Desktop/resume/bigywater/a/c/d ← 真实拿当前工作目录拼出绝对路径,且../真的被折叠了(b/../c变成了c)真实的原因在于.和..在语义上完全不是同一类符号:.永远真实等价于"当前这一层目录",不管文件系统实际结构是什么,直接去掉它不会改变路径的真实含义,所以Path在构造/打印时就真实会做这一步轻量的字符串层面清理;..的含义是"上一级目录",它真实指向的具体位置取决于它前面那一段路径在真实文件系统里到底是什么(如果b是个符号链接,b/..可能真实跳到跟字面上看起来完全不同的地方),单纯在字符串层面折叠..可能会算出一个真实错误的路径,所以Path对象本身不会自作聪明地帮你做这件事,只有显式调用resolve()(真实会调用系统的realpath,需要真实访问文件系统、跟随符号链接)才会给出一份真正折叠过..、语义上一定正确的绝对路径。
每一步都是本文上面用真实的pathlib跑出来的原始结果。
pathlib.Path