
干了十来年运维和测试我电脑里存的最多的东西不是文档而是脚本。从最早在 Windows 上写批处理到后来在 Linux 上写 Shell再到现在用 Python 搭测试框架、用 Playwright 跑 UI 自动化自动化这条线差不多贯穿了我整个职业生涯。很多人把“自动化”和“脚本”当成两个独立话题在聊可我始终觉得它们从来就是一件事自动化是目标脚本是当前投入产出比最高的实现手段。这篇内容我想结合自己在服务器运维、接口测试、UI 测试和日常办公里攒下的经验把这套东西从头捋一遍说说脚本怎么选、怎么写、怎么排错以及那些常规文档里不太会写出来的坑。如果你正打算入门自动化或者已经在写脚本但总遇到一些说不清的问题这篇文章应该能帮上忙。我不会只讲某个工具的命令行用法而是用真实工作场景串起来讲毕竟自动化不是背命令而是解决问题的思维方式。1. 自动化与脚本先分清目标与工具1.1 自动化的本质把重复劳动交给机器自动化的本质说白了就是把“人按照固定规则重复做的事”交给机器去做。人做重复劳动容易疲劳、走神、出错机器不会。你可以把自动化理解成一台洗衣机你不必关心内部是怎么进水、排水、转动的只要把衣服放进去、按一个按钮剩下的事交给机器。脚本就是那个“按钮”背后的一整套操作说明书。自动化的应用范围比大多数人想象的要大。从硬件设备的长时间老化测试到服务器的日志清理和磁盘监控从接口层的回归验证到 UI 层面的点击操作再到办公室里的报表统计、文件搬运本质上都是同一件事。热搜里那批词什么设备老化测试全自动执行脚本、网络设备自动化运维脚本、AI自动化办公本质上都是在各自的领域里把重复劳动转交给机器执行。我见过不少人一上来就追求“全自动”恨不得系统自己会思考。但真正成熟的自动化是一步一步长出来的。早期阶段哪怕是半自动、甚至只是在命令行里帮你拼好一条命令都能节省大量时间。先跑起来再变聪明。1.2 脚本最小成本的自动化起点脚本在自动化里是最轻量的一种形态。它不需要编译写完就能跑解释器读一行执行一行。相比动辄几万行代码的软件系统脚本更像是“一次性工具”就像你家里工具箱中的订书机需要的时候拿起来咔嗒一下用完就放回去。什么场景适合直接用脚本处理我的判断标准很简单任务规则清晰、规模不大、不需要长期频繁迭代、执行频率属于小时级或天级。比如清理临时文件、批量改文件名、定时备份数据库、轮询一个接口看有没有异常这些都是脚本的典型应用场景。当你发现脚本开始越写越长里面有大量 if else 分支、模块拆分、配置文件、异常处理并且需要多个人一起维护时才需要考虑引入框架或者更完整的工程化体系。这个界限不用太纠结经验是先乱写写到痛了再重构比一开始就上框架要实在得多。1.3 明确自动化边界避免过度设计过度自动化是新手最容易犯的毛病。明明一个月才执行一次的任务非要搭一套带 Web 界面的调度平台明明两行批处理能搞定的事非要写一个带数据库记录的 Python 工程。结果就是自动化省下的时间全赔进了维护自动化的时间里。我给自己定过一个判断标准叫“自动化三问”第一这件事发生的频率够不够高一个月都不发生一次不值得自动化第二规则够不够固定每次都要人工判断怎么处理自动化帮不上忙第三失败之后的影响能不能承受影响很大的任务哪怕频率高也要先做成半自动让人在最后一步把关。这套标准帮我挡住了很多次“拍脑袋自动化”的冲动。记住一句话自动化的目的是释放时间而不是制造新的负担。2. 脚本语言怎么选先看场景再谈技术2.1 ShellLinux 服务器管理的万能胶如果你要打交道的机器是 Linux 服务器Shell 几乎是绕不开的第一选择。它最大的优势是跟系统命令天然打通ls、find、grep、awk、tar、curl这些命令在 Shell 里可以像积木一样拼接使用。写一个脚本本质上就是在组织一堆现成的系统命令让它们按顺序、按条件协作。举一个很常见的例子批量处理文件用 for 循环#!/usr/bin/env bash for file in /data/logs/*.log; do if [[ -f $file ]]; then gzip $file fi done看起来简单但里面有几个关键点。变量引用要加双引号防止文件名带空格导致命令被拆成多段[[ ]] 比 [ ] 更安全支持正则和更丰富的判断循环体里最好只做一件事逻辑复杂就拆函数。Shell 脚本运行中如果命令失败了默认不会停下来而是继续往下跑这很容易埋雷。所以在脚本开头写一句 set -euo pipefail是我多年的习惯意思是遇到错误就退出、变量没定义就报错、管道中任何一环失败就算整体失败。但 Shell 也有明显的短板处理复杂 JSON、做复杂的字符串操作、写算法逻辑时非常难受。判断要不要用 Shell就看任务是不是以“调用系统命令、处理文件和进程”为主。是用 Shell不是换 Python。2.2 Python数据处理与接口自动化的主力Python 是自动化领域的事实标准语言尤其在接口测试、数据处理、爬虫、办公自动化这几个方向。它的优势是生态太丰富了你要的功能几乎都有现成的库requests 处理 HTTP、pandas 处理表格、pytest 做测试框架、Playwright 做浏览器自动化。语法接近自然语言读起来像在描述流程而不是在跟机器较劲。接口自动化测试是我日常工作中最常用到 Python 的场景。一个基本框架只需要三样东西requests 负责发请求pytest 负责组织和断言再配一个报告插件展示结果。网上聊“java接口自动化测试框架”的人也很多Java 的生态同样强大但 Python 胜在写起来快、调试成本低特别适合测试团队这种“代码量不大但经常改”的场景。如果你做办公自动化Python 也是首选。操作 Excel 用 openpyxl操作 Word 用 python-docx处理 PDF 有 PyPDF2收发邮件有 smtplib。哪怕你是做硬件的Python 也能用来控制串口、读取传感器数据。我自己做设备老化测试脚本时很多硬件状态采集逻辑就是用 Python 写的配合 GUI 界面做成一个带了点交互的工具比纯命令脚本要好用得多。2.3 PowerShellWindows 环境下的系统管家长期在 Windows 服务器上做运维的人离不开 PowerShell。它跟 Shell 类似但更偏向 Windows 生态管理服务、计划任务、IIS、AD 域、Exchange它能直接对着系统对象操作这是 CMD 批处理做不到的。有一个常见的坑第一次使用 PowerShell 运行脚本时经常会遇到“因为在此系统上禁止运行脚本”的报错。原因是 Windows 默认执行策略是 Restricted什么都不允许跑。你需要用管理员身份打开 PowerShell执行Set-ExecutionPolicy RemoteSigned这个策略的意思是本地写的脚本可以运行从网上下载的脚本必须有可信签名。比起直接把策略改成 UnrestrictedRemoteSigned 兼顾了便利和安全。养成习惯不管在哪个环境执行策略能开多小就开多小够用就行。PowerShell 里操作任务计划也很方便。比如创建一个每天凌晨两点运行的清理任务schtasks /create /tn LogCleaner /tr powershell -File C:\scripts\clean_logs.ps1 /sc daily /st 02:00这条命令我经常在运维文档里用它比在“任务计划程序”图形界面里点半天要高效得多。2.4 RPA 与商业工具不会写代码也能做自动化如果你完全不会写代码也不想学RPA 工具是另一条路。像是影刀、按键精灵这类工具通过拖拽组件和录制操作就能实现一些网页点击、表单填写、文件整理的自动化流程。适合办公场景里的重复操作比如每天早晨登录后台导出报表、按固定格式填入 Excel、再发送到企业微信群。RPA 的价值在于门槛低业务人员自己也能上手不用求着 IT 部门排期。但它的短板也很明显流程稍微复杂就会很脆网页结构一变、按钮位置一换流程可能就断了。我的建议是如果你能用 Python 写脚本大部分 RPA 能做的事你都能做而且更可控如果实在没有时间学编程RPA 作为过渡方案完全可行。现在很多 RPA 工具也开始内置 AI 能力支持用自然语言描述流程这确实在降低自动化的门槛。3. 从零搭一个自动化脚本日志清理与告警案例3.1 需求定义先拆清楚再动手讲了一堆理论该来点实在的。我用一个自己真实做过的场景来完整演示一遍某台服务器上的应用每天产生大量日志磁盘使用率已经多次冲到 80% 以上再涨下去会影响服务稳定性。需求拆解下来有四条第一删除超过 7 天的原始日志文件第二删除之前把日志先压缩归档避免误删后无法追溯第三压缩包只保留 30 天过期就删第四每次清理完通知一下运维群让大家知道磁盘情况。这个需求看起来简单但拆解这一步特别重要。很多人拿到的需求是“把日志清理一下”如果不做拆解就会在写脚本时把所有想法揉在一起。拆清楚之后你会发现每一部分都是独立的逻辑后面写代码就顺了。还有一个细节为什么是 7 天和 30 天这是我跟业务方商量出来的。日志一般只需要保留一周用于排查问题压缩包作为历史留档保留一个月足够。参数不要写死在命令里而是定义成变量后面要调整时只改一处就行。3.2 脚本实现关键命令背后的逻辑直接上一个完整的 Shell 脚本这个版本我在生产环境跑过很久逻辑相对完整#!/usr/bin/env bash set -euo pipefail LOG_DIR/data/applogs BACKUP_DIR/data/logbackup KEEP_DAYS7 KEEP_TAR_DAYS30 WEBHOOK_URLhttps://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyxxxx mkdir -p $BACKUP_DIR # 进入日志目录用相对路径避免 tar 打包时提示 Removing leading / cd $LOG_DIR # 找出超过 KEEP_DAYS 天且以 .log 结尾的文件 mapfile -t old_logs (find . -type f -name *.log -mtime $KEEP_DAYS) # 有需要处理的文件才打包 if ((${#old_logs[]} 0)); then tar_namelogs_$(date %Y%m%d_%H%M%S).tar.gz tar czf $BACKUP_DIR/$tar_name ${old_logs[]} printf %s\n ${old_logs[]} | xargs rm -f fi # 删除超过保留期的压缩包 find $BACKUP_DIR -type f -name *.tar.gz -mtime $KEEP_TAR_DAYS -delete # 计算磁盘使用率并发送通知 disk_usage$(df -h $LOG_DIR | awk NR2 {print $5}) curl -s -H Content-Type: application/json \ -d {\msgtype\:\text\,\text\:{\content\:\[自动清理完成] $LOG_DIR 当前使用率 $disk_usage\}} \ $WEBHOOK_URL /dev/null逐个说下关键点。set -euo pipefail 必须放在脚本开头。没有它脚本中途任何一条命令失败都会静默继续最后的告警消息可能看起来一切正常实际上清理根本没成功。find 命令里的 -mtime 7 表示“修改时间超过 7 天”单位是天。注意 7 和 7 含义不同7 是超过 7 天7 是正好 7 天这个区间0 是 24 小时内。很多人在这里踩坑以为写 7 就是保留 7 天实际上写 7 才对。mapfile 配合进程替换把 find 的结果读入数组。这里有个隐藏问题如果日志文件特别多一次性读入内存会占用不少资源。文件量到了几十万级别时更稳的做法是用 find -print0 配合 xargs -0 边找边处理。我这个场景日志量不大数组方式反而清晰。打包路径我特意先 cd 到 LOG_DIR 再 find这样传给 tar 的是相对路径。如果直接用绝对路径tar 会提示 Removing leading /意思是把路径开头的 / 去掉了解压的时候文件结构会跟原来的预期不一致。提前规避这个坑日志会干净很多。最后用 curl 发企业微信机器人消息。这里要注意curl 的 -d 参数里包含中文和特殊字符在现代系统上一般没有问题。如果遇到编码问题可以把内容先转 base64再在消息里拼接。3.3 调度落地让脚本按时自己跑脚本写好了手动执行没问题接下来要做的是定时调度。Linux 下最常见的方案是 crontab。编辑当前用户的定时任务crontab -e加入一行0 2 * * * /opt/scripts/clean_logs.sh /var/log/clean_logs.log 21这一段里“0 2 * * *”代表每天凌晨 2 点 0 分执行五个字段依次是分钟、小时、日期、月份、星期。日志输出一定要重定向到文件否则 cron 会把输出内容以邮件形式发给本地用户时间一长邮箱会被塞爆。大家常说的“脚本定时任务没执行”多半就是死在环境变量上cron 执行时的 PATH 环境变量非常精简只有 /usr/bin:/bin你脚本里如果用到自己装的命令比如 /usr/local/bin 下的工具就会提示 command not found。解决办法是在脚本里重新设置 PATHexport PATH/usr/local/bin:/usr/bin:/bin:$PATHWindows 上等效的方案是任务计划程序或者用前面提到的 schtasks 命令行创建。两种系统思路完全一致定义执行时间、指定要执行的脚本、记录输出结果。到了更复杂的生产环境还可以用 systemd timer 替代 cron它支持更细粒度的依赖和日志管理但对大多数场景来说cron 已经足够。3.4 结果验证与迭代别写完就丢脚本上线不是终点。我第一次写完这个日志清理脚本手动执行后自认为没问题结果第二天发现企业微信群里压根没有消息。排查以后发现当天需要清理的文件数量是 0脚本里判断了数组为空就不打包、不删除告警命令在 if 块之外本来应该执行但当时文件多我测试的是有日志的情况漏测了空库的场景。这个经历教会我一个习惯写完脚本至少要做三组测试。第一组是正常场景有文件可清理第二组是边界场景没有文件可清理确认脚本不会误报第三组是异常场景比如日志目录不存在、磁盘满了、Webhook 地址无效确认脚本至少会报错而不是假装成功。调试时用 bash -x 脚本名能看到每一行命令的实际执行情况和变量值比在代码里到处加 echo 要直观得多。跑几天后每天看一眼日志输出确认数据正常这个脚本才算真正交付。4. 从脚本到自动化测试框架pytest 与 Playwright 落地思路4.1 为什么脚本够用时还要框架写测试和写脚本最大的区别在于脚本通常是“跑一次就完”而测试要“反复跑、跑很多次、跑出报告、让团队都看得懂”。如果你只是偶尔手动调用一个接口检查返回结果用 requests 写个几十行的临时脚本就够了。但当接口数量多起来、用例量到几百条、还要跟 CI 集成时你就需要一个框架来管三件事用例收集与执行、执行前后的准备和清理、失败时的断言与报告。pytest 是目前 Python 生态里最主流的测试框架。它做的事情可以理解成一个舞台导演帮你找到所有以 test 开头的函数按规则排序执行执行之前调用你定义的准备工作执行之后做清理最后汇总成一份报告。你写的那些测试函数反而只是演员。4.2 用 pytest 搭一个接口自动化骨架我拿登录 token 的复用为例这是接口自动化里最常见的需求。每个接口都要带 token如果每个测试用例都重新登录一次效率低还可能触发服务端的限流策略。用 fixture 可以解决import pytest import requests pytest.fixture(scopesession) def token(): # 整个测试会话只执行一次 resp requests.post( https://api.example.com/login, json{user: demo, pwd: 123456}, timeout10 ) assert resp.status_code 200 return resp.json()[token] pytest.mark.parametrize(keyword,expected, [ (apple, 0), (banana, 1), (cherry, 0), ]) def test_search(token, keyword, expected): headers {Authorization: fBearer {token}} resp requests.get( https://api.example.com/search, params{q: keyword}, headersheaders, timeout10 ) assert resp.status_code 200 assert resp.json()[code] expected这段代码里有两个精髓。第一个是 fixture 的 scopesession它告诉 pytest这个 token 在本次测试会话里只生成一次所有用到的用例共享同一个返回值。如果你不写 scope默认是 function每个函数都会重新登录一次。很多人用法用错了写出的代码慢得要命就是这个作用域没搞清楚。第二个是 parametrize 参数化。三组数据共用同一个测试函数测试报告里会显示成三条用例。这样做的最大好处是测试数据和测试逻辑分离后续新增一组测试数据只需要往参数列表里加一行不需要复制粘贴函数。团队里不会写代码的同事也能轻松维护用例数据。4.3 浏览器自动化Playwright 与移动端对策UI 自动化是大家最感兴趣的领域。早期 Selenium 确实很流行但 WebDriver 这套协议本身就老旧响应慢安装要匹配浏览器的驱动版本经常升级一次浏览器就全线崩盘。相比之下Playwright 已经成为我现在的首选。它内置了浏览器驱动不需要额外安装统一 API 同时支持 Chromium、Firefox、WebKit最关键的进步是自动等待机制。一个最小的 Playwright 脚本长这样from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessTrue) page browser.new_page() page.goto(https://example.com) page.get_by_role(button, name登录).click() page.locator(#search_input).fill(自动化) page.locator(#search_button).click() page.wait_for_selector(.result-list) print(page.title()) browser.close()这里最值得讲的是等待策略。老一代 UI 自动化脚本里最常见的问题就是用 time.sleep(3) 固定等三秒结果网络慢的时候元素还没渲染出来就报错了。Playwright 的 locator 操作自带智能等待点击按钮之前它会自动等待元素可见、可点、稳定最大限度消除网络波动带来的随机失败。移动端 UI 自动化情况会复杂一些。Appium 是老牌方案支持 iOS 和 Android但它环境配置重、执行速度慢。如果你只想快速做移动 App 的核心流程验证可以看看 Maestro它的脚本是 YAML 写成的上手成本极低。选择依据看你团队的规模需要跨平台、深度集成系统能力的选 Appium追求快、重视简单体验的选 Maestro。4.4 稳定性的核心等待、断言与数据隔离自动化测试最令人头疼的不是写不出来而是“这次过了、下次没过、什么也没改动”。我总结下来大概率出在三个地方。第一是等待。UI 自动化里凡是写死 time.sleep 的地方都是潜在的不稳定点。正确的思路是等“条件成立”等元素可见、等接口返回、等某段文本出现。宁可超时给一个明确报错也不要让脚本在错误的页面状态里继续跑下去否则后续所有断言都会失败而且失败原因面目全非。第二是断言。断言不是越多越好而是要断言用户能感知到的核心结果。比如登录成功你要断言页面上出现了用户名而不是断言一个内部跳转 URL。断言太表面问题漏掉断言太内部跟 UI 细节耦合改个样式就全挂。找一个中间平衡点断言“用户可感知的、业务不可妥协的结果”。第三是数据隔离。自动化测试最忌讳跟真实数据混在一起。跑一次测试创建了一堆脏数据第二天再跑数据状态就不是预期状态了用例就会失败。合理的做法是每个用例使用独立账号或者独立数据前缀执行前清理现场执行后恢复现场。pytest 的 fixture 就是干这个的fixture 的 teardown 部分专门留给你做清理。这个习惯一定要从一开始就养成。5. 自动化脚本常见坑与排查手册5.1 “无法识别为 cmdlet、函数、脚本文件或可运行程序的名称”这个报错太经典了尤其出现在 pnpm、node、python 这类命令上。第一次遇到的人往往以为是软件没装成功其实多数情况下是安装路径没有加入环境变量 PATH。以 Windows 上 pnpm 报错为例按下面的顺序排查基本能定位第一步在终端里执行 where pnpm看系统能不能找到这个命令。如果输出“找不到文件”说明 PATH 里完全没有它。第二步执行 npm prefix -g查出全局 node_modules 所在目录一般是 C:\Users\你的用户名\AppData\Roaming\npm。第三步打开系统环境变量设置把上面这个目录追加到 PATH 里保存后重开一个终端窗口。这里有个细节修改 PATH 之后已打开的老终端不会自动刷新一定要开新窗口否则依然报同样的错。如果你用的是较新版本的 Node.js也可以试试 corepack enable pnpm它能通过 Node 自带的 corepack 管理 pnpm省去手动配置环境变量的步骤。这条经验适用于任何“命令找不到”的场景“无法识别”不只代表缺安装也代表缺环境变量两个方向都要看一眼。5.2 Windows 脚本闪退先看错误信息再谈修复双击 .bat 或 .ps1 文件闪退背后原因大概率是脚本执行时报错了窗口在错误信息显示之前就被系统关掉。有两个办法看到真实错误第一个是临时在脚本最后加一行 pause让窗口执行完停在原地等待按键第二个是打开 cmd 或 PowerShell直接运行脚本文件路径错误信息会留在终端里。排查过程中你会发现很多报错来自路径。Windows 的 Program Files 目录带空格如果你的命令写成 C:\Program Files\SomeTool\run.exe不加引号系统会把它拆成两段第一段是 C:\Program第二段是 Files\SomeTool\run.exe自然不是有效程序。正确写法是给整个路径加双引号“C:\Program Files\SomeTool\run.exe”。中文乱码也是 Windows 脚本的老问题。批处理默认编码如果是 Unicode而 win 终端默认的代码页是 936GBK运行就会乱码。在脚本中加一句 chcp 65001 nul 切换到 UTF-8多数场景能解决如果还不行把脚本另存为 ANSI 编码或者 UTF-8 with BOM基本就稳定了。5.3 Shell 脚本执行怪错CRLF 换行符在作怪你在 Windows 上写好的 Shell 脚本传到 Linux 服务器上执行报出 $\r: command not found 这类错原因几乎可以锁定为换行符差异。Windows 文本文件的行尾是回车加换行CRLF而 Linux 只认换行LF。脚本里的每一行末尾多了个 \r 字符Shell 会把它当成命令的一部分。解决方式有两种。一种是用 dos2unix 命令直接转换dos2unix script.sh。另一种不用额外装工具直接用 sed 删除行尾的回车sed -i s/\r$// script.sh以后写脚本我建议在 VSCode 或任意编辑器里把默认换行符设置成 LF。编辑器的右下角一般能看到当前文档的换行符点一下改成 LF 再保存。养成这个习惯比每次上传服务器后再转换要省事得多。5.4 cron 日志黑洞与告警静默定时任务“没执行”最怕的不是报错而是静默。脚本自己跑挂却没有留下任何痕迹排查时完全摸不着头脑。我吃过的亏多了以后总结了两条铁律。第一条cron 任务的输出一定要重定向到日志文件不要偷懒。每一条定时任务都写成这样0 2 * * * /opt/scripts/clean.sh /var/log/clean.log 21到这一步脚本的输出和错误输出全部进日志。下次怀疑脚本没执行时先看这个日志文件的修改时间再往前翻内容。如果日志最后一条是昨天的那说明今天 cron 就没拉起脚本方向就转到 crond 服务状态上去了。第二条脚本里设置好 PATH。cron 环境里 PATH 极为精简你手动登录时正常运行的命令在 cron 下很可能就是 command not found。在每个自动化脚本开头重新声明一次 PATH是成本最低的防御。5.5 自动化不是越多越好最后说点技术之外的话。自动化是工具工具要放在合适的地方用也要知道什么地方不该用。有些操作需要人来判断和拍板硬自动化反而会放大风险有些规则本身还在频繁变自动化做完第二天就要跟着改还不如手工来得灵活还有些事从一开始就不该写成脚本比如那些用来作弊、绕过规则和验证机制的脚本。这类脚本不止是道德问题它带来的账号封禁、法律风险、数据安全风险远超那一点省下来的时间。我在团队里反复讲一句话写脚本前先问自己一句这件事如果被公开了我能不能坦然。如果答案是否定的那就别写。我个人现在做一个自动化方案之前习惯先花一半时间想清楚“什么不该自动化”。把不该做的那部分剔除掉剩下该做的部分往往就清晰了。这篇内容可能偏长但里面每一个模块都是我踩过坑之后沉淀下来的。自动化的学习路径其实很直白先选一个高频重复的小事试着写成脚本跑起来再加上定时调度然后在这个过程中不断积累排错经验。技术会迭代工具会更换但“把确定性动作交给机器”这个思路永远不会过时。