1
2
3
4
5
6
7
作者:李晓辉

联系方式:

1. 微信:Lxh_Chat

2. 邮箱:939958092@qq.com
  1. Python 系列(一) 从零开始认识 Python,入门其实没那么难
  2. Python 系列(二) 学 Python 的第一步,把开发环境搭建好
  3. Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
  4. Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
  5. Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
  6. Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
  7. Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
  8. Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
  9. Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
  10. Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
  11. Python 系列(十一) list、tuple、set、dict,到底该怎么选?
  12. Python 系列(十二) List 列表实战,搞定数据增删改查与批量处理
  13. Python 系列(十三) Tuple 元组实战,搞懂不可变特性与 enumerate() 遍历
  14. Python 系列(十四) Set 集合实战,搞定数据去重与集合运算
  15. Python 系列(十五) Dict 字典详解,掌握快速查找与键值操作
  16. Python 系列(十六) sort() 和 sorted() 详解,掌握数据排序技巧
  17. Python 系列(十七) 自定义函数入门,让重复代码一次编写多次使用
  18. Python 系列(十八) 函数参数详解,搞懂位置参数、关键字参数和默认参数
  19. Python 系列(十九) 从 *args 到 **kwargs,让函数调用更灵活
  20. Python 系列(二十) 变量作用域是什么?一文搞懂 global 和 nonlocal
  21. Python 系列(二十一) 搞懂变量、对象和引用,理解函数参数传递机制
  22. Python 系列(二十二) map() 和 filter() 详解,轻松实现批量数据处理与筛选
  23. Python 系列(二十三) 嵌套函数与 lambda 表达式详解,搞懂作用域与闭包
  24. Python 系列(二十四) 搞懂递归函数,从基线条件到实际应用
  25. Python 系列(二十五) 聊聊模块与 import,让代码告别重复编写
  26. Python 系列(二十六) Python 模块入门,从 import 到模块复用
  27. Python 系列(二十七) Python dir() 函数详解,快速查看模块中的函数与属性
  28. Python 系列(二十八) 不会用 Python 标准库?教你快速查找模块和函数
  29. Python 系列(二十九) 搞懂 Python sys 模块,让脚本更适合自动化运维
  30. Python 系列(三十) 为什么 0.1 + 0.2 不等于 0.3?彻底搞懂 Decimal
  31. Python 系列(三十一) 搞懂 datetime 日期时间,轻松处理时区与时间计算
  32. Python 系列(三十二) 面向对象编程入门,从零理解类、对象、封装与继承
  33. Python 系列(三十三) 什么是类和对象?零基础理解 Python class
  34. Python 系列(三十四) Python 属性详解,搞懂类属性、实例属性和 setter
  35. Python 系列(三十五) 学会特殊方法,让对象打印出你想要的内容
  36. Python 系列(三十六) 多个对象如何共享数据?彻底搞懂类变量
  37. Python 系列(三十七) 搞懂父类、子类和方法重写,真正理解继承与多态
  38. Python 系列(三十八) 如何判断数据类型?三个内置函数一次讲明白
  39. Python 系列(三十九) 搞懂异常模型与常见异常类型,读懂程序报错
  40. Python 系列(四十) 从零学会异常处理,掌握 try、except、else 和 finally
  41. Python 系列(四十一) ValueError、TypeError 到底是什么?一文搞懂常见异常
  42. Python 系列(四十二) ValueError、TypeError 到底是什么?一文搞懂常见异常
  43. Python 系列(四十三) 别只会处理异常!学会自定义异常,让程序更容易维护
  44. Python 系列(四十四) 从零认识 assert 断言,让程序学会检查自己
  45. Python 系列(四十五) 使用 sys 模块处理标准输入与输出
  46. Python 系列(四十六) 使用 open() 读取和写入文件
  47. Python 系列(四十七) 学会读写文本文件,让程序保存运行结果
  48. Python 系列(四十八) 搞懂 bytes、bytearray 和二进制读写
  49. Python 系列(四十九) 使用 seek() 和 tell() 灵活控制文件读取位置
  50. Python 系列(五十) 掌握 os 和 os.path,实现文件与目录自动化管理
  51. Python 系列(五十一) 掌握列表推导式,轻松实现列表生成与数据筛选
  52. Python 系列(五十二) 掌握字典推导式,让字典创建更简单
  53. Python 系列(五十三) 从零理解生成器,轻松处理大量数据
  54. Python 系列(五十五) 使用 zip() 同时遍历多个集合

前面我们学习了如何使用 Python 读取文件、写入文件,以及通过 seek() 和 tell() 定位文件中的内容。

这些操作解决了文件内容的问题,但在日常工作中,我们还经常需要处理另一类任务。

例如:

  • 想知道当前 Python 程序运行在哪个目录中。
  • 想查看某个目录下有哪些文件。
  • 想判断一个配置文件是否存在。
  • 想创建一个目录,专门保存程序生成的日志。
  • 想删除不再需要的临时文件。
  • 想清理已经完成任务的空目录。
  • 想批量检查 Linux 服务器上的配置文件是否齐全。

如果每次都手动打开目录、查找文件、创建目录或者删除文件,不仅效率低,而且很难自动化处理大量文件。

能不能让 Python 帮我们完成这些工作?

当然可以。Python 提供了 os 和 os.path 等工具,帮助我们获取目录信息、检查文件是否存在、创建目录、删除文件,以及处理文件路径。

这篇文章就从最基础的操作开始,逐步学习如何使用它们管理文件和目录。

认识 os 和 os.path

在学习具体代码之前,我们先理解这两个名称分别代表什么。

os 是 Python 提供的一个标准库模块,可以理解为 Python 与操作系统打交道的工具箱。通过它,我们可以执行一些与文件、目录、环境变量和当前工作目录有关的操作。

os.path 则主要负责处理文件路径,例如拼接路径、获取文件名、提取目录名,以及判断路径是否存在。

可以简单理解为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
os
|
+-- 查看目录中的内容
|
+-- 创建或删除目录
|
+-- 删除文件
|
+-- 获取当前工作目录

os.path
|
+-- 拼接文件路径
|
+-- 获取文件名和目录名
|
+-- 判断文件或目录是否存在

这两个模块都属于 Python 标准库,不需要单独安装。

查看当前目录下有哪些文件

假设你正在维护一台 Linux 服务器,某个目录中保存着应用程序的配置文件、日志文件和脚本。

如果想知道这个目录中有哪些内容,可以使用 os.listdir()。

创建一个名为 os_demo.py 的文件,写入以下代码:

1
2
3
4
import os

files = os.listdir(".")
print(files)

理解 import os

1
import os

import 可以理解为导入工具。

通过 import os,我们就可以在程序中使用 os 模块提供的功能。

理解 os.listdir()

1
files = os.listdir(".")

listdir() 用于获取指定目录中的内容名称,其中 . 表示当前工作目录。

假设当前目录中有以下文件:

1
2
3
4
5
python-demo/
├── os_demo.py
├── app.py
├── config.ini
└── access.log

运行程序:

1
python os_demo.py

可能得到类似的结果:

1
['os_demo.py', 'app.py', 'config.ini', 'access.log']

实际输出顺序可能不同,具体内容也取决于目录中的实际文件。

os.listdir() 返回一个列表,其中保存了目录下的文件名和子目录名。它只列出当前目录这一层的内容,不会自动进入子目录继续查找。

另外,返回的名称通常不包含完整路径。如果需要进一步操作文件,还要根据实际情况拼接路径。

获取当前工作目录

有时候,我们不清楚 Python 程序究竟从哪个目录开始运行。

这时,可以使用 os.getcwd()。

1
2
3
4
5
import os

current_dir = os.getcwd()

print("当前工作目录:", current_dir)

运行程序:

1
python os_demo.py

假设当前工作目录是 /home/python-demo,输出可能是:

1
当前工作目录: /home/python-demo

getcwd() 是 get current working directory 的缩写,可以理解为获取当前工作目录。

这里需要区分两个概念:

  • 当前工作目录:程序解析相对路径时使用的目录。
  • Python 脚本所在目录:Python 脚本文件实际保存的位置。

两者不一定相同。

例如,你可以在 /home 目录下运行:

1
python /opt/scripts/os_demo.py

此时,脚本位于 /opt/scripts,但当前工作目录仍可能是 /home。

这也是一些初学者明明写对了文件名,程序却提示找不到文件的常见原因。

使用 os.path 拼接文件路径

假设我们需要处理 /var/log 目录中的 messages 文件。

可以直接写出完整路径:

1
2
log_file = "/var/log/messages"
print(log_file)

但如果目录和文件名经常变化,每次都手动拼接路径就很麻烦。

这时可以使用 os.path.join()。

1
2
3
4
5
6
7
8
import os

directory = "/var/log"
filename = "messages"

file_path = os.path.join(directory, filename)

print(file_path)

在 Linux 系统上,输出为:

1
/var/log/messages

这段代码可以分成三个步骤:

  • directory 保存目录路径。
  • filename 保存文件名。
  • os.path.join() 按照当前操作系统的路径规则组合它们。

手动拼接字符串也能实现类似效果,但如果需要兼容 Windows,路径分隔符就可能带来问题。

使用 os.path.join() 可以让 Python 根据当前操作系统选择合适的路径分隔符。

需要注意,os.path.join() 只负责拼接路径,并不会检查目标文件是否真实存在。

获取文件名和目录名

假设我们拿到一个完整路径:

1
/var/log/nginx/access.log

我们可能只想获取文件名 access.log,或者获取它所在的目录 /var/log/nginx。

这时可以使用 os.path.basename() 和 os.path.dirname()。

1
2
3
4
5
6
7
8
9
import os

file_path = "/var/log/nginx/access.log"

filename = os.path.basename(file_path)
directory = os.path.dirname(file_path)

print("文件名:", filename)
print("目录名:", directory)

运行结果:

1
2
文件名: access.log
目录名: /var/log/nginx

这两个方法的作用分别是:

  • basename():获取路径最后一部分的名称。
  • dirname():获取路径中的目录部分。

它们主要处理路径字符串,不要求对应文件真实存在。

判断文件或目录是否存在

假设我们编写了一个日志分析程序,需要读取 access.log。

如果文件不存在,程序就可能无法正常运行。因此,在读取文件之前,可以先判断目标路径是否存在。

使用 os.path.exists():

1
2
3
4
5
6
7
8
import os

file_path = "access.log"

if os.path.exists(file_path):
print("目标路径存在")
else:
print("目标路径不存在")

如果当前工作目录中没有 access.log,运行结果就是:

1
目标路径不存在

如果文件存在,则会输出:

1
目标路径存在

exists() 既可以判断文件,也可以判断目录。

如果想进一步区分路径究竟是文件还是目录,可以使用另外两个方法。

判断路径是否为文件

使用 os.path.isfile():

1
2
3
4
5
6
7
8
import os

path = "access.log"

if os.path.isfile(path):
print("这是一个文件")
else:
print("这不是一个普通文件,或者路径不存在")

判断路径是否为目录

使用 os.path.isdir():

1
2
3
4
5
6
7
8
import os

path = "logs"

if os.path.isdir(path):
print("这是一个目录")
else:
print("这不是一个目录,或者路径不存在")

这三个方法的区别如下:

方法作用
os.path.exists(path)判断路径是否存在
os.path.isfile(path)判断路径是否指向文件
os.path.isdir(path)判断路径是否指向目录

需要注意,路径存在并不意味着程序一定有权限读取它,也不代表后续操作一定成功。

创建目录

假设我们正在编写一个自动巡检脚本,希望把每次运行的结果保存到 reports 目录中。

如果目录不存在,就需要先创建它。

创建单个目录

可以使用 os.mkdir():

1
2
3
4
5
import os

os.mkdir("reports")

print("目录创建完成")

如果当前目录中还没有 reports,运行成功后会创建这个目录。

但如果 reports 已经存在,再次运行代码就可能出现 FileExistsError 异常。

创建多层目录

如果需要创建下面这样的目录结构:

1
2
3
reports/
└── 2026/
└── october/

可以使用 os.makedirs():

1
2
3
4
5
import os

os.makedirs("reports/2026/october", exist_ok=True)

print("目录已准备好")

这里的 exist_ok=True 表示:如果目标目录已经存在,就不因为目录已存在而报错。

这很适合用在需要反复执行的自动化脚本中。

不过,如果目标路径存在但不是目录,或者程序没有相应权限,仍然可能发生异常。

删除文件

目录创建好了,接下来学习另一个常见操作:删除文件。

在 Linux 运维中,我们可能需要删除已经处理完成的临时文件、过期的测试文件,或者不再需要的程序输出文件。

Python 可以使用 os.remove() 删除文件。

例如,当前目录中有一个 temp.txt 文件:

1
2
3
python-demo/
├── os_demo.py
└── temp.txt

我们希望通过 Python 删除它,可以编写以下代码:

1
2
3
4
5
6
7
8
9
import os

file_path = "temp.txt"

if os.path.isfile(file_path):
os.remove(file_path)
print("文件删除成功")
else:
print("文件不存在,无法删除")

运行程序:

1
python os_demo.py

如果 temp.txt 存在,输出为:

1
文件删除成功

删除后,目录结构变为:

1
2
python-demo/
└── os_demo.py

理解 os.remove()

1
os.remove(file_path)

remove() 用于删除指定路径对应的文件。

需要注意,删除操作会直接影响文件系统。对于普通文件,删除成功后,文件名就不再指向原来的文件。

如果文件不存在,或者程序没有删除权限,操作就可能引发异常。

另外,os.remove() 不能用来删除一个非空目录。

特别提醒: 删除文件前应确认路径正确,尤其是在服务器自动化脚本中,不要直接对未经检查的用户输入执行删除操作。上面的示例通过 os.path.isfile() 做了基础检查,但在复杂环境下,还需要考虑符号链接、权限、路径变化等情况。

删除空目录

如果我们想删除的是目录,而不是文件,可以使用 os.rmdir()。

例如,当前目录中存在一个空的 old_logs 目录:

1
2
3
python-demo/
├── os_demo.py
└── old_logs/

可以这样删除:

1
2
3
4
5
6
7
8
9
import os

directory = "old_logs"

if os.path.isdir(directory):
os.rmdir(directory)
print("空目录删除成功")
else:
print("目录不存在")

运行结果:

1
空目录删除成功

为什么只能删除空目录

os.rmdir() 的用途是删除空目录。

如果 old_logs 里面还有文件,例如:

1
2
3
old_logs/
├── app.log
└── error.log

执行:

1
os.rmdir("old_logs")

就会因为目录不为空而失败,通常会引发 OSError。

这样设计可以避免程序在删除目录时,无意中将里面的文件一起删除。

如果你只想删除空目录,os.rmdir() 就很合适。

删除非空目录

如果目录里面还有文件和子目录,但我们确实需要将整个目录删除,就需要使用 shutil 模块提供的 shutil.rmtree()。

例如:

1
2
3
4
5
old_logs/
├── app.log
├── error.log
└── archive/
└── old.log

如果希望删除整个 old_logs 目录,可以这样写:

1
2
3
4
5
6
7
8
9
10
import os
import shutil

directory = "old_logs"

if os.path.isdir(directory):
shutil.rmtree(directory)
print("目录及其内容已删除")
else:
print("目录不存在")

运行成功后,old_logs 目录及其内部文件、子目录都会被删除。

这里多了一个新模块:

1
import shutil

shutil 是 Python 标准库中用于执行高级文件操作的模块,除了删除目录树,还支持复制文件、复制目录等操作。

rmdir() 和 rmtree() 有什么区别

方法用途是否允许目录非空
os.rmdir()删除空目录不允许
shutil.rmtree()删除目录及其内部内容允许

重要警告: shutil.rmtree() 可能一次性删除大量文件,通常不会把文件放入回收站。不要在不确认路径的情况下使用它,更不要直接对 /、/var、/home 等重要系统目录执行删除操作。

实际运维脚本中,应当明确限定待删除目录,必要时增加路径白名单、人工确认和日志记录。仅仅检查 os.path.isdir() 并不能保证目标目录就是你想删除的目录。

删除操作的异常处理

前面的示例已经对文件和目录是否存在做了检查,但检查与删除之间仍然可能发生变化。

例如,文件可能被其他程序删除,或者当前用户没有足够权限。

因此,在实际程序中,还可以使用 try 和 except 处理异常。

下面以删除文件为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import os

file_path = "temp.txt"

try:
os.remove(file_path)
print("文件删除成功")
except FileNotFoundError:
print("文件不存在")
except PermissionError:
print("没有权限删除该文件")
except OSError as error:
print("删除文件时发生错误:", error)

如果文件不存在,程序会输出:

1
文件不存在

如果删除成功,则会输出:

1
文件删除成功

这里的 try 表示尝试执行一段可能发生错误的代码,except 则用于捕获并处理对应异常。

最后的 OSError 用来处理其他常见的操作系统层面错误,例如文件被占用时可能出现的问题、文件系统限制等。

前面我们已经学习过异常处理,因此这里把它应用到实际文件操作中。

实战:准备巡检报告目录并清理临时文件

现在把前面学习的知识组合起来,编写一个简单的运维辅助脚本。

假设程序需要完成以下任务:

  • 创建 reports 目录,保存巡检结果。
  • 检查 temp.txt 是否存在。
  • 如果存在,则删除这个临时文件。
  • 如果不存在,则提示用户。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import os

# 创建报告目录,如果目录已经存在则不报错
os.makedirs("reports", exist_ok=True)
print("报告目录已准备好")

# 检查并删除临时文件
temp_file = "temp.txt"

try:
if os.path.isfile(temp_file):
os.remove(temp_file)
print("临时文件已删除")
else:
print("临时文件不存在,无需删除")
except PermissionError:
print("没有权限删除临时文件")
except OSError as error:
print("删除临时文件时发生错误:", error)

运行程序:

1
python os_demo.py

如果 reports 目录不存在,并且 temp.txt 也不存在,输出可能是:

1
2
报告目录已准备好
临时文件不存在,无需删除

如果 temp.txt 存在并且可以删除,输出可能是:

1
2
报告目录已准备好
临时文件已删除

整个过程如下:

flowchart TD
    A["程序启动"] --> B["创建 reports 目录"]
    B --> C["检查 temp.txt"]
    C --> D{"文件存在吗?"}
    D -->|不存在| E["提示无需删除"]
    D -->|存在| F["尝试删除文件"]
    F --> G{"删除成功吗?"}
    G -->|成功| H["提示删除成功"]
    G -->|失败| I["捕获异常并提示"]
    E --> J["程序结束"]
    H --> J
    I --> J

这个例子还比较简单,但已经包含了自动化脚本常见的处理流程:准备工作目录、检查目标对象、执行操作,并处理可能发生的错误。

在真正的生产环境中,如果要清理日志或备份文件,还需要增加保留期限、文件匹配规则、路径校验和操作日志等机制,避免误删重要数据。

常用方法汇总

学习到这里,我们已经接触了不少方法。可以通过下面的表格快速回顾。

方法作用
os.listdir(path)列出目录中的内容
os.getcwd()获取当前工作目录
os.mkdir(path)创建单个目录
os.makedirs(path, exist_ok=True)创建多层目录,并允许目录已存在
os.remove(path)删除文件
os.rmdir(path)删除空目录
os.path.join(a, b)拼接路径
os.path.basename(path)获取路径最后一部分的名称
os.path.dirname(path)获取路径中的目录部分
os.path.exists(path)判断路径是否存在
os.path.isfile(path)判断路径是否为文件
os.path.isdir(path)判断路径是否为目录
shutil.rmtree(path)删除目录及其内部内容

写在最后

这篇文章我们学习了 Python 中用于处理文件和目录的两个重要工具:os 和 os.path,还认识了用于删除非空目录的 shutil 模块。

通过实际示例,我们已经掌握了查看目录、获取当前工作目录、拼接路径、判断文件是否存在、创建目录,以及删除文件和目录等操作。

这些能力是编写自动化运维脚本的重要基础。无论是检查配置文件、整理日志,还是保存巡检报告,都离不开对文件和目录的管理。

不过,文件和目录的删除操作需要格外谨慎:os.remove() 用于删除文件,os.rmdir() 只能删除空目录,而 shutil.rmtree() 会删除目录及其内部内容。使用前一定要确认路径,避免误删重要数据。

到这里,Python 输入与输出相关的内容就基本学习完了。

下一章,我们将进入新的主题:使用紧凑的语法和专用语句来创建、处理和排序数据结构,进一步学习如何用更简洁的方式组织和处理数据。