大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
脚本语言
这一课你将能做到
- 用 Bash 脚本完成项目构建、日志清理、服务器健康检查的自动化
- 用 Python 编写调用云厂商 API 的工具脚本,处理 JSON/CSV 数据
- 能在 Java/Bash/Python 之间选对场景:什么时候用 Java 写业务,什么时候用脚本写运维
为什么要学
你已经学会了 Git 和 Linux,但每次部署还是手动敲命令?那说明你缺一门「胶水语言」——把各种工具串起来的能力。对 Java 后端来说,Bash 是服务器上的必备技能(因为服务器只有 shell),Python 是复杂运维任务的放大器(处理数据、调用 API、写工具)。
人话:Java 适合做「重活」(业务逻辑、高并发),但写个脚本删三天前的日志、拉阿里云 ECS 列表、批量处理 JSON,用 Java 就太重了。DevOps 讲究「选对工具做小事」,脚本语言就是专门用来写这类「小事」的,而且几分钟就能跑起来。
先建立图像
运维自动化的三层能力:
手动 SSH 敲命令 ← 已学(第 2 课)
│
▼ 把命令固化成文件
Bash 脚本(部署、检查、清理) ← 本课第一层
│
▼ 需要条件判断、循环、API 调用
Python 脚本(复杂逻辑、数据处理) ← 本课第二层
│
▼ 需要类型安全、长时间运行、高并发
Go 程序(基础设施级工具) ← 进阶时了解
代码量与运行时间对比:
任务 Bash Python Java
清理三天前的日志 3行 10行 50行+
部署脚本 30行 40行 200行+
调用阿里云 API 查资源 难 20行 80行+
跟着做
步骤 1:写第一个 Bash 部署脚本
把第 1 课的 deploy.sh 扩展为可用版本:
#!/bin/bash
set -euo pipefail # e=遇错退出 u=未定义变量报错 pipefail=管道失败传递
APP_NAME="myapp"
JAR_FILE="target/${APP_NAME}-0.0.1-SNAPSHOT.jar"
SERVER="appuser@1.2.3.4"
REMOTE_DIR="/opt/${APP_NAME}"
BACKUP_DIR="/opt/backups"
echo "=== 1. 本地构建 ==="
mvn clean package -DskipTests -q
if [[ ! -f "$JAR_FILE" ]]; then
echo "构建失败:找不到 $JAR_FILE" >&2
exit 1
fi
echo "=== 2. 备份旧版本 ==="
ssh "$SERVER" "mkdir -p ${BACKUP_DIR} && cp -v ${REMOTE_DIR}/app.jar ${BACKUP_DIR}/app-$(date +%Y%m%d%H%M).jar || true"
echo "=== 3. 上传新版本 ==="
scp "$JAR_FILE" "$SERVER:${REMOTE_DIR}/app.jar"
echo "=== 4. 远程重启并验证 ==="
ssh "$SERVER" << 'REMOTE'
sudo systemctl restart myapp
sleep 5
if curl -sf http://localhost:8080/actuator/health; then
echo "部署成功"
else
echo "健康检查失败,准备回滚" >&2
exit 1
fi
REMOTE
运行前记得:chmod +x deploy.sh,脚本里所有 "" 不要省,否则路径含空格会炸。
步骤 2:Bash 日志清理脚本
#!/bin/bash
# 清理 /var/log/myapp 下超过 7 天的日志,保留压缩文件
LOG_DIR="/var/log/myapp"
KEEP_DAYS=7
# find 的基本语法:在目录里找,条件 -mtime +天数 -type f,动作 -delete/-exec
echo "正在清理 ${KEEP_DAYS} 天前的日志..."
find "$LOG_DIR" -name "*.log" -type f -mtime +$KEEP_DAYS -print
delete_count=$(find "$LOG_DIR" -name "*.log" -type f -mtime +$KEEP_DAYS -delete | wc -l)
# 磁盘使用率大于 85% 时报警
usage=$(df -h "$LOG_DIR" | awk 'NR==2 {gsub(/%/,""); print $5}')
if [[ "$usage" -gt 85 ]]; then
echo "ALERT: 磁盘使用率 ${usage}%,请立即检查 ${LOG_DIR}" >&2
# 可以接 webhook 发到钉钉/飞书
fi
步骤 3:Python 调用阿里云 API 查 ECS 实例
#!/usr/bin/env python3
# pip install alibabacloud-ecs20140526
import json
from alibabacloud_ecs20140526 import Client
from alibabacloud_tea_openapi import models as open_api_models
def list_instances():
config = open_api_models.Config(
access_key_id='YOUR_KEY_ID', # 实际走环境变量:os.environ['ALICLOUD_ACCESS_KEY']
access_key_secret='YOUR_SECRET',
endpoint='ecs-cn-hangzhou.aliyuncs.com'
)
client = Client(config)
resp = client.describe_instances()
for inst in resp.body.instances.instance:
print(f"{inst.instance_id} | {inst.instance_name} | {inst.public_ip} | {inst.status}")
if __name__ == '__main__':
list_instances()
Python 的优势:有阿里云官方 SDK,几行代码就拿到数据;同样的功能用 Bash curl + jq 要三四十行还难调试。
步骤 4:Python 处理日志——找出 Spring Boot 报错次数最多的 TOP 10
#!/usr/bin/env python3
from collections import Counter
import re
logfile = 'application.log'
pattern = re.compile(r'ERROR .*? - (.*)')
errors = []
with open(logfile, 'r', encoding='utf-8') as f:
for line in f:
m = pattern.search(line)
if m:
# 提取异常类名,如 java.sql.SQLException
err = m.group(1).split(':')[0].strip()
errors.append(err)
for err, count in Counter(errors).most_common(10):
print(f"{count:4d} {err}")
Java 后端写这个要 50 行、编译、打包;Python 直接 python3 parse_log.py,改起来也快。
步骤 5:选择合适的语言
| 场景 | 推荐语言 | 理由 |
|---|---|---|
| 构建打包、SSH 部署、日志清理 | Bash | 服务器原生,不依赖额外环境 |
| 调用云 API、处理 JSON/CSV | Python | 库多,写起来快 |
| 高并发采集、长时间后台服务 | Go | 编译型,资源占用低 |
| 核心业务、并发处理、团队熟悉 | Java | 类型安全、生态成熟 |
讲清楚要点
- Bash 脚本是必选项不是可选项:你不可能在服务器上装个 JDK 只为了跑一个清理日志的程序。Bash 无处不在,但超过 100 行或需要复杂数据结构的脚本,请切 Python。
- Python 是运维的瑞士军刀:requests(HTTP)、boto3/alibabacloud(云 API)、pandas(数据处理)、ansible(配置管理)……运维场景几乎都有成熟的库。不需要精通,会用 Google 查 API 就行。
set -euo pipefail是 Bash 的安全底座:-e遇到错误退出,-u未定义变量报错,-o pipefail让管道失败能传递。这三行让你少踩 80% 的坑。- 脚本也要写注释和错误处理:不要写成「能用就行的一次性脚本」,运维脚本生命周期很长,半年后你自己都看不懂。
- 密钥走环境变量,不要硬编码:所有
YOUR_KEY_ID都要替换为从环境变量读取,脚本里永远不要出现明文密码。
你可能会懵的地方
-
Python 2 还是 Python 3? 必须用 Python 3。Python 2 已于 2020 年停止维护,所有主流 Linux 发行版的默认
python3都是 3.8+。脚本首行写#!/usr/bin/env python3保险。 -
Bash 里
""和''有什么区别?""内的变量会被展开(echo "$HOME"→/root),''内纯文本原样保留。文件名和路径变量永远加"",防止空格炸。 -
脚本路径要不要写绝对路径? 在脚本里调用其他命令时,尽量用绝对路径(
/usr/bin/java而非java),因为 cron 或 systemd 启动的环境 PATH 可能不一样。或者脚本开头显式export PATH=/usr/local/bin:$PATH。 -
$?是什么? 上一条命令的退出码。0 表示成功,非 0 表示失败。if mycmd; then ... fi就是判断$?是否为 0。养成「脚本每一大步都检查是否成功」的习惯。 -
cron 定时任务怎么写?
crontab -e编辑,格式:分 时 日 月 星期 命令。例如每天 3 点清理日志:0 3 * * * /opt/scripts/cleanup.sh >> /var/log/cleanup.log 2>&1。注意 cron 环境变量很少,脚本里 PATH 要写全。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
Bash 不写 set -e | 某条命令失败,脚本继续跑,掩盖问题 | set -euo pipefail 放第二行 |
变量不加引号 rm -rf $dir | dir 为空时变成 rm -rf / | "$dir" |
| 密钥硬编码在脚本里 | 泄露风险,历史记录永久存在 | export ALICLOUD_KEY=xxx 后脚本读环境变量 |
| Python 脚本没有 shebang | 直接执行报错 | 第一行 #!/usr/bin/env python3 |
| Bash 脚本超过 200 行还在硬撑 | 难维护、难调试、数组弱 | 切 Python |
卡点急救
- Bash 脚本里中文乱码:脚本保存为 UTF-8,文件头加
export LANG=zh_CN.UTF-8,传输时用scp -O或rsync保证编码。 - Python 模块找不到:
pip install xxx装到了用户目录,但脚本用 root 跑的,路径不同。统一用虚拟环境(python3 -m venv venv),或在系统级安装pip install --system。 - 脚本在终端能跑,放到 cron 里不行:99% 是 PATH 或工作目录不对。cron 里写绝对路径,或在脚本开头显式
cd /opt/app && export PATH=...。
面试常问
-
Bash 里
$*和$@的区别?"$*"把所有参数拼成一个字符串,"$@"保持每个参数独立。传给其他命令时用"$@":mycmd "$@"。 -
怎么让脚本后台长期运行? 简单场景用
nohup ./script.sh &,但更好的做法是用 systemd 管(像第 2 课管 Java 一样),支持自动重启、日志收集、开机自启。 -
Python 做运维有什么优缺点? 优点:库生态极丰富,写起来快,可读性好;缺点:需要运行时环境(服务器要有 Python),纯计算性能不如 Go/Java。对运维任务来说,开发速度远比运行时性能重要。
自测三问
set -euo pipefail每一部分的作用是什么?缺了-u会有什么隐患?- 写一条 Bash 命令:找出 /var/log 下大于 100MB 的文件,按大小排序显示前 10 个。
- 你有一台新服务器需要装 JDK、Nginx、Docker,你会选 Bash 还是 Python 写初始化脚本?为什么?
小结一句话
脚本语言是 DevOps 的效率杠杆:Bash 解决服务器上的「顺手自动化」,Python 解决需要逻辑和数据处理的「复杂自动化」。
延伸
- BashGuide(中文)
- 下一课:网络协议——理解请求从浏览器到 Spring Boot 的完整路径。