如何递归创建目录但不抛 FileExistsError(exist_ok)

8次阅读

管用,但仅当路径已存在且为目录时静默跳过;若路径是文件或父级为文件,仍抛FileExistsError或OSError。

如何递归创建目录但不抛 FileExistsError(exist_ok)

pythonos.makedirsexist_ok 参数到底管不管用?

管用,但只在「目标路径是目录且已存在」时生效;如果路径已存在且是文件,os.makedirs 仍会抛 FileExistsError。这是最常被误解的一点——很多人以为设了 exist_ok=True 就能“完全静默”,结果在部署脚本里突然崩了。

常见错误现象:FileExistsError: [errno 17] File exists: '/path/to/log',而 /path/to/log 实际上是个普通文件(比如日志文件被提前 touch 出来了),不是目录。

  • exist_ok=True 不会覆盖、不尝试删除、不重命名,它只跳过「创建目录」动作本身
  • 如果父路径中某一级是文件(比如 /tmp 被误写成文件),也会立即报错,不会走到最后一级
  • Python 3.2+ 才支持该参数;旧版本需手动 try/except

想真正静默建目录,得先判断路径类型

安全做法是:先用 os.path.exists 检查,再用 os.path.isdir 确认是不是目录,最后决定是否调用 os.makedirs。绕过 exist_ok 的语义盲区。

示例逻辑:

import os 

def safe_makedirs(path): if os.path.exists(path): if not os.path.isdir(path): raise NotADirectoryError(f"Cannot create directory: {path} exists and is not a directory")

已是目录,无需操作

    return os.makedirs(path, exist_ok=True)

  • 不要只靠 os.path.isdir(path) 判断——路径不存在时它也返回 False,容易误删或跳过
  • 异常类型选 NotADirectoryError 更准确,比泛用 Exception 更利于下游捕获处理
  • 如果业务允许覆盖(极少见),需显式 os.unlink(path) + os.makedirs,但务必加注释说明风险

替代方案:pathlib.Path.mkdirexist_ok 行为一致吗?

行为完全一致:Python 3.4+ 的 pathlib.Path.mkdir(parents=True, exist_ok=True) 同样只对「已存在且为目录」静默,遇到同名文件照样抛 FileExistsError

所以换写法不解决问题,只是语法糖。但 pathlib 在组合路径时更健壮(自动处理斜杠、跨平台),推荐用于新项目:

from pathlib import Path 

p = Path("/var/log/myapp") p.mkdir(parents=True, exist_ok=True) # 同样不解决“路径是文件”的情况

  • parents=True 是必须的,否则父目录不存在时直接失败(os.makedirs 默认就是递归
  • 不能用 p.mkdir(exist_ok=True) 代替 p.parent.mkdir(...) 来建父目录——它只作用于 p 自身
  • os.makedirs 一样,不处理权限(mode 参数受 umask 影响),生产环境建议显式设 mode=0o755

Shell 场景下怎么避免 mkdir -p 报错?

linux/macOS 的 mkdir -p 本身就不抛错——它遇到已存在目录直接忽略,遇到同名文件则报 mkdir: cannot create directory ‘xxx’: File exists,行为和 Python 完全对应。

所以 shell 脚本里也得防御性检查:

if [ -e "$DIR" ] && [ ! -d "$DIR" ]; then   echo "Error: $DIR exists but is not a directory" >&2   exit 1 fi mkdir -p "$DIR"
  • 别用 [ -d "$DIR" ] || mkdir -p "$DIR" ——当 $DIR 不存在时,-d 返回 false,会执行 mkdir;但当它是文件时,-d 也返回 false,同样会执行 mkdir 并失败
  • [ -e ][ -d ] 必须分开判断,顺序不能反(先 -e-d
  • zsh/bash 都支持,但 dash(/bin/sh 默认)不支持 [[,坚持用 [ 更稳妥

实际用的时候,最容易被忽略的是:你写的函数或脚本,可能跑在别人已经预置了同名文件的环境里。这时候 exist_ok=True 不是银弹,而是个温柔的陷阱。

text=ZqhQzanResources