承接Shell脚本系列,本章我们正式开启Python运维之旅。如果你已经掌握了Shell基础,学习Python会非常顺畅——两者核心逻辑相通,只是语法和适用场景不同。Shell擅长快速拼接命令实现简单自动化,而Python则能处理更复杂的逻辑、更大量的数据,开发更健壮的运维工具。
本章完全从运维视角出发,不讲冗余的编程概念,先帮你搭好环境,快速掌握运维必备的核心语法,写出第一个实用脚本,搞定最常用的文件目录自动化操作。学完本章,你就能用Python替代部分复杂的Shell脚本,解决Shell写起来费劲的场景。
1.1 认知篇:运维为什么要学Python?与Shell的区别及选型边界
1.1.1 运维学Python的核心价值
很多同学会问:Shell已经够用了,为什么还要学Python?核心原因有四点:
-
复杂逻辑更易维护:几百行以上的Shell脚本可读性差、调试困难,Python语法结构清晰,支持模块化、函数化拆分,复杂业务逻辑写起来更顺畅,后续维护成本更低。运维场景中,往往需要实现复杂的条件判断、多层循环、异常处理等逻辑。Shell虽然能做,但代码会变得冗长且难以理解,特别是在多人维护或代码审查时成为瓶颈。Python的代码可读性强,注释支持更灵活,团队协作效率提升30%以上。
-
生态强大开箱即用:海量成熟的第三方库覆盖所有运维场景——系统监控、远程操作、API交互、数据解析,直接调用即可,不用像Shell那样手动拼接命令造轮子。例如
paramiko库可直接实现SSH远程执行,requests库简化HTTP API调用,yaml库优雅处理配置文件,psutil库获取系统资源实时状态。这些在Shell中需要结合多个命令、复杂管道处理,在Python中仅需几行代码,大幅提升开发效率。 -
跨平台一致性好:Python脚本在Linux、Windows、Mac上运行效果一致,而Shell受Bash/Zsh、发行版差异影响大,跨平台适配成本高。对于大型运维团队或企业级工具,跨平台支持是刚需。Python的os模块自动处理不同系统的路径分隔符、命令执行差异,无需编写平台判断逻辑。
-
运维进阶的必备基础:后续学习Ansible、自动化平台、DevOps工具链,底层大多基于Python,掌握Python才能看懂原理、做二次开发。许多互联网大厂的运维工具、监控系统都用Python构建,了解Python让你快速融入企业级工具链。
1.1.2 Shell与Python的选型边界
不是Python替代Shell,而是两者各司其职、配合使用。
使用场景对照优先用Shell的场景:
- 简单命令拼接(一两条管道)
- 一次性操作、快速验证想法
- 系统初始化、标准化脚本
- 轻量定时任务(cron配合)
- 日志实时过滤(三剑客awk/sed/grep场景)
优先用Python的场景:
- 复杂分支逻辑、多层条件嵌套
- 大量数据统计分析、数据结构化处理
- JSON/XML文件解析与生成
- 跨平台脚本分发、一次开发多处运行
- API交互、HTTP请求处理
- 批量远程管理(100台以上服务器)
- 开发可复用的运维工具、企业级自动化框架
关键决策点:如果脚本预计超过200行或需要维护超过6个月,优先考虑Python。如果纯粹是临时一次性操作,Shell效率更高。
一句话总结:简单自动化用Shell,复杂工具用Python,两者配合是运维的最佳实践。
1.2 环境篇:Linux下Python环境确认与脚本运行方式
运维场景几乎都在Linux下运行Python脚本,我们全程基于Linux环境讲解。主流发行版(CentOS 7+、Ubuntu 18.04+、Rocky Linux、AlmaLinux)都默认预装了Python 3。即使没有,也能通过包管理器快速安装。
1.2.1 确认Python环境
打开终端,输入命令确认版本:
python3 --version正常会输出类似 Python 3.10.12 的版本号,只要是3.6以上版本都兼容本教程内容。
版本选择建议
- Python 3.6 ~ 3.8:适合老旧企业系统,库版本兼容性最好
- Python 3.9 ~ 3.11:推荐选用,生产环境主流版本,性能提升20%
- Python 3.12+:最新特性,但部分企业库还未适配,暂不推荐运维生产环境
1.2.2 pip包管理工具
pip是Python的第三方包管理工具,地位相当于Linux的YUM/APT,用来安装扩展库。
确认pip版本:
pip3 --version# 输出示例:pip 24.0 from /usr/local/lib/python3.11/site-packages/pip运维高频pip命令:
# 安装第三方包pip3 install 包名
# 安装指定版本pip3 install 包名==1.2.3
# 卸载包pip3 uninstall 包名
# 查看已安装的所有包pip3 list
# 导出依赖列表,用于批量部署(环境迁移关键)pip3 freeze > requirements.txt
# 批量安装依赖(新环境快速部署)pip3 install -r requirements.txt国内源配置国内源加速配置:默认官方源下载速度慢,配置清华源大幅提升速度:
terminal pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple也可临时指定源:
Terminal window pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple paramiko
1.2.3 Python脚本的运行方式
和Shell脚本逻辑完全一致,Python脚本也是纯文本文件,惯例后缀为.py。
方式1:python3命令直接执行(最常用)
新建脚本文件后,直接用解释器执行,不需要执行权限:
python3 first.py对应Shell的 bash first.sh,适合临时测试、快速运行。
方式2:加执行权限运行(生产脚本标准写法)
脚本第一行加Shebang指定解释器,然后赋予执行权限,和Shell完全一致:
#!/usr/bin/env python3print("Hello Python!")chmod +x first.py./first.pyShebang最佳实践为什么用
#!/usr/bin/env python3而不是#!/usr/bin/python3?
#!/usr/bin/python3:写死Python路径,如果系统装在非标准位置或使用虚拟环境,脚本运行失败#!/usr/bin/env python3:推荐用法,会自动查找系统PATH中的python3,兼容性最强虚拟环境场景下,后者能自动使用虚拟环境的Python版本,跨系统迁移也无需修改脚本头。
补充:交互式模式
终端输入python3进入交互式解释器,输入一行执行一行,适合临时测试小段代码、理解语言特性:
python3>>> x = 10>>> y = 20>>> print(x + y)30>>> exit()按Ctrl+D或输入exit()退出交互模式。
1.3 语法速通:运维必备核心语法(对照Shell讲解)
如果你已经掌握Shell,这部分会非常好理解——核心逻辑(条件、循环、函数)完全相通,只是语法格式不同。我们全程对照Shell语法讲解,帮你快速迁移知识。
1.3.1 变量与基础数据类型
变量定义规则
- Shell:
name="张三",等号两边绝对不能有空格 - Python:
name = "张三",等号两边推荐加空格(规范写法,PEP8标准)
命名规则和Shell基本一致:由字母、数字、下划线组成,不能以数字开头,区分大小写。
Python变量命名规范遵循PEP8标准提升代码质量:
- 常量全大写:
MAX_RETRIES = 3- 变量小写加下划线:
server_name,log_path- 避免单字母(i除外):用
index替代x- 避免和内置函数重名:不用
list,dict,file作变量名
运维高频核心数据类型
- 字符串(str):文本内容,单引号/双引号效果一致
name = "运维工程师"job = 'DevOps'
# 字符串拼接,对应Shell的 ${a}${b}msg = "你好," + name
# Python推荐用f-string(格式化字符串),比+拼接更灵活server = "web01"ip = "192.168.1.10"info = f"服务器{server}的IP是{ip}"print(info) # 输出:服务器web01的IP是192.168.1.10- 整数(int):数字,直接书写即可
age = 25cpu_threshold = 80disk_free = 100_000 # Python 3.6+支持下划线增加可读性
# 直接算术运算,对应Shell的 $(( ))sum_val = 10 + 20quotient = 100 // 10 # 整除remainder = 100 % 3 # 取余- 列表(list):对应Shell的索引数组,存储一组有序数据
# 定义列表,对应Shell的 services=("nginx" "mysql" "redis")services = ["nginx", "mysql", "redis"]
# 访问元素,下标从0开始,对应Shell的 ${services[0]}print(services[0]) # 输出 nginx
# 获取长度,对应Shell的 ${#services[@]}print(len(services)) # 输出 3
# 追加元素,对应Shell的 services+=("tomcat")services.append("tomcat")
# 遍历列表for svc in services: print(svc)
# 列表切片(强大功能,Shell没有直接对应)print(services[1:3]) # 输出 ['mysql', 'redis']- 字典(dict):对应Shell的关联数组,键值对结构
# 定义字典,对应Shell的 declare -A server_infoserver_info = { "ip": "192.168.1.100", "hostname": "web01", "role": "nginx"}
# 按键取值,对应Shell的 ${server_info["ip"]}print(server_info["ip"]) # 输出 192.168.1.100
# 新增/修改键值server_info["status"] = "running"
# 遍历字典for key, value in server_info.items(): print(f"{key}: {value}")
# 判断键是否存在if "port" in server_info: print(server_info["port"])else: print("键不存在")1.3.2 条件判断
语法核心差异
- Shell:
if [ 条件 ]; then ... fi,靠fi标记结束 - Python:
if 条件:,靠缩进区分代码块(统一4个空格),无结束关键字
运算符对照表
| 功能 | Shell写法 | Python写法 |
|---|---|---|
| 大于 | -gt | > |
| 小于 | -lt | < |
| 等于 | -eq(数值)/ =(字符串) | ==(通用) |
| 不等于 | -ne / != | != |
| 大于等于 | -ge | >= |
| 小于等于 | -le | <= |
| 逻辑与 | -a | and |
| 逻辑或 | -o | or |
| 逻辑非 | ! | not |
| 文件存在 | [ -f file ] | os.path.exists(file) |
| 目录存在 | [ -d dir ] | os.path.isdir(dir) |
| 字符串非空 | [ -n "$str" ] | if str: |
多分支示例
score = 85if score >= 90: print("优秀")elif score >= 80: print("良好")elif score >= 60: print("及格")else: print("不及格")
# 运维常见的服务状态检查status = "running"if status == "running": print("服务正常")elif status == "stopped": print("服务已停止")else: print("服务异常")新手第一高频坑缩进错误是Python初学者最常见的失误。Python严格要求同一层级代码对齐,统一用4个空格,绝对禁止混用Tab和空格。许多编辑器Tab默认是8个空格,容易造成混乱。建议:
- 编辑器设置 Tab = 4个空格自动转换
- 开启显示空白符(可视化缩进问题)
- 使用IDE的自动格式化工具(Ctrl+Shift+F)
1.3.3 循环结构
break、continue的用法和Shell完全一致:break终止整个循环,continue跳过当前一轮。
for循环(运维最高频)
用于批量遍历列表、序列,对应Shell的for i in xxx。
services = ["nginx", "mysql", "redis"]for svc in services: print("正在检查服务:", svc)
# 运维场景:批量检查多个服务for svc in services: if svc == "nginx": print(f"重启 {svc}...") # 这里调用重启命令 continue print(f"检查 {svc} 状态")range()函数:生成连续数字序列,对应Shell的{1..10}
# 遍历1到10(range左闭右开,10不包含)for i in range(1, 11): print(i)
# 从0开始,步长为2for i in range(0, 20, 2): print(i) # 输出 0, 2, 4, 6, ... 18
# 运维场景:重试机制for attempt in range(1, 4): print(f"第 {attempt} 次尝试连接...") # 连接逻辑 if attempt == 3: print("连接失败,最后一次尝试")while循环
条件为真就持续循环,用法和Shell完全一致。
i = 1while i <= 5: print("第", i, "次检测") i = i + 1
# 运维场景:监控告警循环retry_count = 0while retry_count < 3: try: # 执行风险操作 result = connect_to_server() break # 成功则退出循环 except Exception as e: retry_count += 1 print(f"连接失败,准备第 {retry_count} 次重试")1.3.4 函数定义
语法核心差异
- Shell:
函数名() { ... },参数靠$1/$2接收,return只能返回0-255状态码 - Python:
def 函数名(参数):,靠缩进区分函数体,return可以返回任意类型数据
带参数带返回值示例
# 定义加法函数def add(a, b): result = a + b return result
# 调用并接收返回值sum_result = add(10, 20)print(sum_result) # 输出 30
# 运维场景:检查端口是否开放def check_port(host, port): """检查服务器指定端口是否可达""" try: import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex((host, port)) sock.close() return result == 0 # 返回True/False except Exception as e: print(f"检查端口异常: {e}") return False
# 调用if check_port("192.168.1.10", 22): print("SSH服务可达")else: print("SSH服务不可达")Python函数的核心优势相比Shell的状态码返回,Python函数的return更灵活强大:
- 可返回字符串、列表、字典等复杂数据结构
- 支持多个返回值:
return success, error_msg, result_data- 支持None表示操作失败,无需特殊的数字编码
- 支持异常处理(try/except),比Shell的error handling优雅得多
这些特性让Python实现复杂业务逻辑时,代码更简洁、更易维护。
1.4 实战上手:第一个运维脚本——批量重命名文件
我们来写第一个实用的运维脚本:批量给指定目录下的.txt文件加上当前日期前缀,比如test.txt变成20260706_test.txt。这是运维日常高频需求,用Python实现逻辑清晰、兼容性好。
完整脚本代码
#!/usr/bin/env python3# 功能:批量给txt文件添加日期前缀import osfrom datetime import datetime
# ========== 配置项 ==========target_dir = "./test_files" # 目标目录date_prefix = datetime.now().strftime("%Y%m%d") # 生成日期前缀,如20260706
# ========== 前置检查 ==========if not os.path.isdir(target_dir): print(f"错误:目标目录 {target_dir} 不存在") exit(1)
# ========== 批量重命名 ==========count = 0for filename in os.listdir(target_dir): # 只处理 .txt 后缀的文件 if filename.endswith(".txt"): # 拼接完整路径,自动处理分隔符 old_path = os.path.join(target_dir, filename) new_filename = f"{date_prefix}_{filename}" new_path = os.path.join(target_dir, new_filename)
# 执行重命名 os.rename(old_path, new_path) print(f"已重命名:{filename} -> {new_filename}") count += 1
print(f"\n处理完成,共重命名 {count} 个文件")关键代码说明
import os:导入系统操作核心库,对应Shell的内置文件命令datetime.now().strftime("%Y%m%d"):生成日期字符串,对应Shell的date +%Y%m%dos.path.isdir():判断目录是否存在,对应Shell的[ -d ]os.listdir():列出目录下所有文件名,对应Shell的lsos.path.join():安全拼接路径,自动适配不同系统的分隔符os.rename():重命名文件,对应Shell的mv
测试提示运行前先新建
test_files目录,放入几个测试txt文件,避免直接在生产目录操作:terminal mkdir test_filestouch test_files/log1.txt test_files/report.txtpython3 batch_rename.pyls test_files/
1.5 核心基础:文件与目录自动化操作(os模块核心用法)
运维80%的场景都在和文件、目录打交道,os模块就是Python中对应Shell文件操作的核心工具库。下面按场景分类讲解高频用法,每个都对应Shell命令,方便对照记忆。
1.5.1 路径与属性判断
对应Shell的文件测试表达式:
| 功能 | Shell写法 | Python写法 |
|---|---|---|
| 判断文件/目录是否存在 | [ -e path ] | os.path.exists(path) |
| 判断是否为目录 | [ -d path ] | os.path.isdir(path) |
| 判断是否为普通文件 | [ -f path ] | os.path.isfile(path) |
| 判断是否为符号链接 | [ -L path ] | os.path.islink(path) |
| 获取文件大小(字节) | stat -c %s path | os.path.getsize(path) |
| 获取绝对路径 | realpath path | os.path.abspath(path) |
| 获取文件夹名 | basename /path/to/dir | os.path.basename(path) |
| 获取路径部分 | dirname /path/to/file | os.path.dirname(path) |
1.5.2 目录操作
对应Shell的mkdir、cd、pwd等命令:
import os
# 获取当前工作目录,对应 pwdcurrent_dir = os.getcwd()print(f"当前目录:{current_dir}")
# 切换工作目录,对应 cd /optos.chdir("/opt")
# 创建单个目录,对应 mkdir dataos.mkdir("data")
# 递归创建多级目录,对应 mkdir -p a/b/cos.makedirs("a/b/c")
# 删除空目录,对应 rmdir dataos.rmdir("data")
# 列出目录内容,对应 lsitems = os.listdir(".")for item in items: print(item)目录删除操作
os.rmdir()只能删除空目录,删除非空目录(对应rm -rf)使用shutil模块:remove_dir.py import shutil# 删除非空目录及所有内容,等价于 rm -rf# ⚠️ 生产环境慎用,无法恢复shutil.rmtree("target_directory")# 更安全的做法:先检查再删除import ostarget = "target_directory"if os.path.exists(target):if os.path.isdir(target):shutil.rmtree(target)print(f"已删除目录:{target}")else:os.remove(target)print(f"已删除文件:{target}")
1.5.3 文件操作
对应Shell的cp、mv、rm命令:
import osimport shutil
# 复制文件,对应 cp src.txt dst.txtshutil.copy("src.txt", "dst.txt")
# 复制文件并保留元数据(修改时间等),对应 cp -pshutil.copy2("src.txt", "dst.txt")
# 递归复制目录,对应 cp -r src_dir dst_dirshutil.copytree("src_dir", "dst_dir")
# 移动/重命名文件,对应 mv old new# shutil.move更安全,可跨分区shutil.move("old.txt", "new.txt")
# 删除文件,对应 rm file.txtos.remove("file.txt")
# 删除非空目录,对应 rm -rfshutil.rmtree("directory")1.5.4 遍历目录
1. 单层遍历:os.listdir()
只遍历目标目录第一层,不进入子目录,对应ls。
# 列出当前目录下所有文件和目录for item in os.listdir("."): full_path = os.path.join(".", item) if os.path.isdir(full_path): print(f"[目录] {item}") else: print(f"[文件] {item}")2. 递归遍历:os.walk()
递归遍历所有子目录,对应find的遍历能力,是日志分析、批量文件处理的核心。
# 递归查找/var/log下所有.log文件,等价于 find /var/log -type f -name "*.log"for root, dirs, files in os.walk("/var/log"): # root: 当前遍历到的目录路径 # dirs: 当前目录下的子目录列表 # files: 当前目录下的文件列表 for file in files: if file.endswith(".log"): full_path = os.path.join(root, file) # 获取文件大小 size = os.path.getsize(full_path) print(f"{full_path} ({size} bytes)")
# 运维场景:查找所有超过1GB的日志文件并清理import osfor root, dirs, files in os.walk("/var/log"): for file in files: if file.endswith(".log"): full_path = os.path.join(root, file) size = os.path.getsize(full_path) # 1GB = 1073741824 bytes if size > 1073741824: print(f"清理大日志:{full_path} ({size/1024/1024:.2f}MB)") os.remove(full_path)os.walk()运维妙用
os.walk()在日常运维中非常高频:
- 日志清理:递归找出超大日志文件,自动删除或压缩
- 备份验证:遍历备份目录,校验文件完整性
- 配置文件扫描:查找所有配置文件,批量修改配置项
- 权限检查:递归检查目录权限,修复不合规权限
1.6 本章小结 + 入门动手练习
1.6.1 本章知识点梳理
-
选型认知:Shell适合简单命令拼接与快速自动化,Python适合复杂逻辑、数据处理与工具开发,两者配合效率最高。关键决策:脚本超过200行或维护期限超过6个月,优先选Python。
-
环境运行:Linux默认预装Python3,通过pip3安装第三方库,脚本支持解释器执行和加权限直接运行两种方式。国内用户配置清华源大幅加速包下载。
-
核心语法:
- 变量:等号两边可加空格,核心类型为字符串、整数、列表、字典,推荐使用f-string进行字符串格式化
- 条件判断:靠缩进区分代码块,使用标准比较符与and/or/not逻辑运算,掌握文件/目录存在性判断
- 循环:for循环用于批量遍历,range()生成数字序列,while循环用于条件持续,break/continue用法与Shell一致
- 函数:def关键字定义,return可返回任意类型数据(比Shell的0-255状态码灵活得多),支持异常处理
-
文件目录操作:os模块为核心,覆盖所有Shell文件操作命令,os.walk()可实现递归目录遍历,是日志处理、批量操作的利器。
1.6.2 动手练习(巩固所学)
练习1:日志文件分类
编写脚本,遍历/var/log目录,按照日期分类日志文件到不同目录:
/var/log/├── 2025-01/│ ├── syslog│ └── auth.log├── 2025-02/└── ...思路提示
- 使用
os.walk()递归遍历日志目录- 通过文件修改时间
os.path.getmtime()提取日期- 根据日期用
datetime.fromtimestamp()转换成年月- 用
os.makedirs()创建对应的年月目录- 用
shutil.move()移动文件到对应目录
练习2:服务进程监控脚本
编写脚本实现以下逻辑:
- 定义要监控的服务列表(nginx, mysql, redis等)
- 逐一检查服务是否运行
- 未运行则自动启动,并记录日志
思路提示
- 使用
os.system()或subprocess模块执行系统命令- 通过
systemctl status判断服务状态- 用函数实现模块化,方便复用
- 用列表存储服务名称,循环检查
- 未运行则
systemctl start启动
这两个练习涵盖了本章所有核心知识点,完成它们意味着你已掌握Python运维脚本的基础能力。