自动化与脚本,这两个词放在一起,多少有点江湖气。外行听起来像是黑客电影里的暗号,内行知道这就是把重复劳动交给机器的基本盘。我干了这么多年自动化相关的工作,从最早的按键精灵到现在的 pytest、Ansible、Playwright,说句实话,所有工具的本质都是一样的:把人类从繁琐、易错、重复的操作中解放出来,用确定性的代码逻辑去替代不确定的人为操作。这篇内容算是我这些年折腾自动化与脚本的一个阶段性总结,聊聊不同领域的脚本怎么选、自动化测试框架怎么落地、运维脚本怎么写才不翻车,以及那些新手最容易踩的坑。
这篇东西没有固定的阅读对象,不管你是刚接触 shell 脚本的运维新手,还是准备搭建接口自动化测试框架的测试开发,只要你想用脚本解决实际问题,都能从里面找到点能直接拿去用的东西。内容会比较杂,但每一块我都会讲清楚原理和实操细节,毕竟光会复制粘贴代码,不理解背后的逻辑,换一个环境照样抓瞎。
1. 自动化脚本的底层逻辑:先把语言选对,再谈效率
很多新手上来就问"我想学自动化,应该学 Python 还是学 shell",这其实是个伪命题。脚本语言没有绝对的好坏,只有合不合适的场景。选错了语言,后面写起来全是痛苦。
1.1 不同脚本语言的适用场景拆解
我习惯把日常用的脚本语言分成三类,这样分清楚之后选型就不纠结了。
第一类是系统原生脚本,典型代表是 Bash(Linux/macOS)和 PowerShell(Windows)。这类脚本的优势在于零依赖,系统自带,不需要装任何解释器,直接就能跑。比如你登录一台 Linux 服务器,想快速看看日志里的报错次数、批量改个配置、定时清理过期文件,用 shell 脚本是最快的。PowerShell 在 Windows 上就更强了,它能直接调 Windows 的 API,操作注册表、管理服务、配置 IIS,这些都是 Python 做起来很别扭的事情。
第二类是通用脚本语言,典型代表是 Python。它强在生态,你想要的库基本都有。做自动化测试有 pytest,做接口请求有 requests,操作 Excel 有 openpyxl,做数据处理有 pandas。Python 适合的场景是跨平台、逻辑复杂、需要大量第三方库支持的任务。
第三类是特定领域脚本,比如 JavaScript(用于浏览器插件和网页自动化)、Nyquist(音频处理)、AutoIt(Windows GUI 自动化)。这类脚本是"一个萝卜一个坑",你只有在特定领域才会用到。
实际工作里最合理的方式是混用。我的习惯是:系统运维层面能用 shell 解决的坚决不引入 Python,避免在服务器上维护一堆包依赖;测试和数据处理层面用 Python,因为它的代码可读性和生态确实更好;Windows 环境下的系统管理和 GUI 自动化优先 PowerShell,毕竟它天生就长在 Windows 上。
1.2 选型背后的关键权衡点
选脚本语言不是凭喜好,有几个硬指标需要权衡。
第一个是环境依赖。公司服务器通常很干净,装 Python 可能牵扯到系统 Python 版本、虚拟环境、权限等问题,而 shell 脚本是天然存在的。你在写一个只能跑一次的运维脚本时,shell 是最省事的。但如果这个脚本要跑一年,还要支持并发、重试、邮件通知,那 shell 的代码量会迅速膨胀,不如 Python 来得清晰。
第二个是跨平台需求。如果你写的脚本既要在 Windows 跑,又要在 Linux 跑,那 shell 和 PowerShell 都不行,Python 或者 Node.js 是最佳选择。Python 里用os.path.join替代硬编码路径分隔符,就能基本做到一套代码两边跑。
第三个是调试友好度。这个经常被新手忽略。Bash 脚本一旦出错,报错信息经常让人一头雾水,尤其是变量为空导致命令参数错位的问题,排查起来很痛苦。PowerShell 的报错相对友好,但它的语法反人类,管道对象的引用方式我就见过很多人死活绕不过来。Python 的报错和调试体验是最好的,这也是我推荐把复杂逻辑用 Python 实现的核心原因。
注意:不要在脚本里硬编码路径!我见过太多人写死
/home/user/xxx,换一台机器就秒挂。好的做法是脚本开头用变量定义所有路径,甚至通过环境变量或配置文件传入,这样才算真正可移植的脚本。
2. 自动化测试与框架:pytest、Appium、Playwright、Maestro
自动化测试是脚本应用最密集的领域,也是很多测试同学入行自动化的第一站。这四个框架我都有实际使用经验,它们覆盖了接口测试、安卓 App 测试、Web E2E 测试和移动端 UI 测试四大块。
2.1 测试框架选型对照:按目标场景来选择
我整理了一份我自己的选型清单,包含四个主流框架的定位、适用场景和经验心得,直接照着选就行。
| 框架 | 类型 | 适合场景 | 语言 | 个人经验 |
|---|---|---|---|---|
| pytest | 单元/接口测试框架 | 后端接口测试、数据驱动测试 | Python | 生态最强,fixture 机制无比强大,配上报插件和 hook 能玩出花 |
| Appium | 移动端 UI 自动化 | 安卓/iOS App 的 UI 功能回归 | 多语言 | 基于 WebDriver 协议,兼容性广,但环境搭建繁琐,慢 |
| Playwright | Web E2E 测试 | 浏览器端全链路测试、爬虫 | 多语言 | 微软出品,自带浏览器驱动,不用额外装 WebDriver,速度快,稳定 |
| Maestro | 移动端 UI 自动化 | 快速移动 App UI 测试 | YAML | 极度轻量,写 YAML 流式定义步骤,不需要代码编程能力 |
2.2 pytest 从零到落地的关键细节
pytest 是我最常用的测试框架,没有之一。它的核心价值在于 conftest.py 和 fixture。新手容易犯的错是:所有逻辑都写在测试函数里,断言失败之后连日志都没有。正确做法是用 fixture 管理前置条件和清理动作,用 pytest.ini 管理配置。
举个例子,一个接口自动化项目的标准目录结构我通常会这样组织:
test_api/ ├── pytest.ini # 配置文件 ├── conftest.py # 全局 fixture ├── utils/ │ ├── __init__.py │ ├── requests_client.py # 封装的请求客户端 │ └── assert_utils.py # 断言工具 └── testcases/ ├── test_user.py # 用户模块测试 └── test_order.py # 订单模块测试关键知识点是 fixture 的 scope。@pytest.fixture(scope="session")级别的 fixture 在整个测试会话期间只执行一次,适合初始化数据库连接、获取 token;scope="function"则在每个测试函数前执行,适合创建独立测试数据。我曾见过有人把登录逻辑写在每一个测试函数里,结果跑一次全量测试光登录就花了几分钟,还经常因为并发问题互相顶掉 session。用 conftest.py 定义 session 级别的登录 fixture 后,问题彻底解决。
另外一个实用技巧是 pytest 的-x(遇到第一个失败即停止)和--maxfail。CI 里跑大量用例时,全部跑完再出结果效率太低,往往会崩溃。我在 Jenkins 里统一配置pytest -x --maxfail=3,一旦连续失败三次立刻熔断,配合邮件通知,能把定位问题的周期压缩到十分钟以内。
2.3 移动端自动化:Appium 与 Maestro 的取舍
移动端自动化最近几年变化挺大,Appium 一家独大的局面正在被打破,Maestro 这类新型框架开始蚕食市场。
Appium 适合对能力要求高的场景,比如深度手势操作、多设备并发、混合应用(H5 与原生混用)。它的环境搭建确实麻烦,需要 JDK、Android SDK、Appium Server、设备驱动,光环境变量就能折腾一下午。但它的兼容性是优势,iOS 和安卓两平台都能打,还能做真机云测。
Maestro 则完全是另一种思路。它不需要写代码,用 YAML 定义每一步操作。官方教学视频里常见的工作流就是:打开 App -> 点击按钮 -> 断言文本出现。这套东西适合快速验证需求,但不适合很复杂的业务链路过长的场景。而且 Maestro 目前主要针对安卓,对 iOS 的支持还不算特别好。
我的建议是:团队里如果已经积累了大量 Appium 脚本,不要随便迁移,迁移成本远高于收益;新项目或者小型团队,可以从 Maestro 起步。另外,不论哪个框架,移动端 UI 自动化的最大敌人永远是两个:元素定位不稳定和等待时机不对。最好的解法是用driver.wait和轮询等待逻辑,而不是固定 sleep。只要涉及移动端 UI 自动化,等待机制一定是安全感的来源。我习惯把所有等待都封装成工具方法,页面加载等待、元素可见等待、可点击等待,而不是在业务代码里裸写 sleep。
2.4 玩转 Web 自动化:Playwright 在我这里的地位
Playwright 是我目前做 Web E2E 测试的首选,因为它解决了 Selenium 历史遗留的痛点:不用自己维护 driver 版本,自带自动等待,还能做浏览器上下文录制。
核心操作是它的 Codegen 功能。执行playwright codegen后,它会打开一个浏览器窗口,你手动操作的每一步都会被录制成代码,生成到目标语言测试文件里。听起来很美好,但实际用下来的经验是:Codegen 生成的代码只是骨架,定位器(locator)经常写得过于脆弱。比如它默认会用文本定位page.click("text=提交"),页面上一旦有多个"提交"按钮就歧义了。我但凡遇到这种,一律改成page.get_by_role("button", name="提交")或者稳定的>- name: Deploy web service hosts: webservers become: yes vars: app_version: "2.3.1" tasks: - name: Pull latest code git: repo: 'git@github.com:myrepo/webapp.git' dest: /var/www/webapp version: "{{ app_version }}" - name: Install dependencies pip: requirements: /var/www/webapp/requirements.txt virtualenv: /var/www/webapp/venv - name: Restart service systemd: name: webapp state: restarted
注意这里的声明式写法:你描述的是最终状态,而不是具体的命令。无论这个服务是首次部署还是重复执行,Playbook 都会收敛到同一个目标状态。我最开始在写 Ansible 时犯过一个典型错误:在任务里写 shell 命令,比如shell: echo "1" >> /etc/nginx/conf.d/xxx.conf,结果每次执行都加一行,重复执行几次配置文件就炸了。后来改成熟练使用lineinfile模块,幂等性才得到保障。
3.2 基础设施模板化:PVE 与 cloud-init 实战
热词里有"pve 9.0 + debian 13 + cloud-init 自动化虚拟机模板实战",这块我实际搞过,属于基础设施自动化的进阶玩法。
传统环境下创建一个新虚拟机,流程是:下载镜像 -> 安装系统 -> 手动配置 IP、主机名 -> 装软件 -> 再做成模板。整套流程下来,没个一两个小时搞不定。用 cloud-init 之后,整个过程被压缩到几分钟,而且配置完全自动化。
核心思路是:先把一个有基本系统和工具的虚拟机模板做好,然后在克隆模板时通过 cloud-init 注入元数据和网络配置。具体来说,cloud-init 会读取来自 NoCloud 数据源或 OpenStack 元数据服务的配置,自动完成主机名设置、用户创建、SSH 密钥注入、软件源配置等操作。
在 PVE 里的操作大致是:
- 创建一个 Debian 13 虚拟机,安装好 cloud-init 包。
- 执行云初始化设置,配置用户名、SSH 公钥、网络段。
- 关闭虚拟机,右键转换为模板。
- 后续克隆时,在 PVE 界面的 Cloud-Init 选项卡里填写每个虚拟机专属的主机名和 IP。
这一步操作完,再配合 Ansible,你就能做到:克隆模板自动起来一台配置好网络的新机器,然后 Ansible Playbook 立刻接管安装业务软件。整个"申请一台新服务器并配置好"的流程,从人工一小时缩短到自动五分钟。基础设施即代码的意义就在于此。
注意:cloud-init 模板里的镜像源地址要提前切换到内网或国内可用源,否则首次启动时拉取软件包会非常慢,甚至超时。我踩过这个坑,虚拟化平台节点在国外源不可达时,新机器不是起不来,而是起来后卡在 apt 更新上,浪费大量时间。
3.3 设备老化测试与网络设备自动化运维脚本
设备老化测试这个场景偏硬件,但脚本在其中扮演的角色同样关键。我参与过的一个路由器老化测试项目,需要让设备连续运行 7 天,每分钟记录一次 CPU 和内存占用、丢包率、温度,出现异常时自动发送告警。这种测试如果人工盯,七天七夜没人受得了,全自动执行脚本是唯一可行方案。
实现方案不复杂,核心是一个 while 循环加时间戳日志:
import time import subprocess from datetime import datetime DURATION = 7 * 24 * 60 * 60 # 7天 INTERVAL = 60 # 每秒采样一次,实际跑用60秒 start_time = time.time() while time.time() - start_time < DURATION: timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") cpu = get_cpu_usage() # 自定义封装:读取 /proc 或 snmp mem = get_memory_usage() ping_result = subprocess.run(["ping", "-c", "1", "-W", "2", gateway], capture_output=True) log_to_csv(timestamp, cpu, mem, ping_result.returncode) if ping_result.returncode != 0: send_alert(f"gateway unreachable at {timestamp}") time.sleep(INTERVAL)网络设备自动化运维,我建议优先考虑使用厂商提供的现成接口。现在主流厂商都支持 RESTCONF 或 NETCONF,脚本通过安全通道调用接口就可以完成配置下发和状态采集。如果你面对的是几十台设备,用 Python 的 netmiko 库批量登录导出配置再到模板里批量改参数即可;设备数量成百上千时,再考虑引入 Ansible 的网络模块。核心逻辑是同一个:集中管理、变更可审计、回归可控。
4. 脚本开发实战:排错思路与高危报错实录
写脚本过程中,排错占的时间远比写代码多。尤其对新手来说,看到一个报错就懵,完全不知道从哪里下手。我整理了几个典型的"高危报错"场景,每个都是我在实际工作中遇到的,把排查思路和解决方法一并写出来。
4.1 "无法将 xxx 识别为 cmdlet、函数、脚本文件或可运行程序的名称"
这个报错在 Windows PowerShell 用户里面出现的频率极高,热词里不管是 pnpm 还是 claude 都碰到了。报错的根源基本只有一个:命令对应的可执行文件不在当前 PATH 环境变量里。
排查步骤按照顺序来:
- 先确认该软件是否真的装了。如果你装的是 Node.js,检查
node -v能不能正常输出。 - 如果装了,但不行,找到软件的安装目录。比如 pnpm 在 Windows 下通常会装到
C:\Users\你的用户名\AppData\Roaming\npm或自定义路径。 - 查看当前 PATH 是否包含该目录。PowerShell 里执行
echo $env:Path查看所有路径。 - 把这个路径手动追加到用户环境变量 PATH 中。注意别用
set PATH=...这种临时方式,这是当前进程生效,一关窗口就没了。正确做法是用[Environment]::SetEnvironmentVariable("Path", "$env:Path;C:\path\to\your\npm", "User")。
另外还有一个高频误判点:PowerShell 出于安全考虑,默认禁止执行未签名的 .ps1 脚本,报错内容与"无法识别"完全不同,但有些人混淆了。如果你看到的是"禁止运行脚本",那执行的策略是 ExecutionPolicy,和 PATH 无关。修改的方式是:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser意思是本地创建的脚本可以运行,从网络下载的必须经过签名验证。这是日常使用里安全性最平衡的方案。
4.2 脚本闪退与弹窗秒关问题
Windows 下写脚本,双击 .bat 或 .ps1 后闪退是经典问题。多数情况下脚本本身报错了,但 cmd 窗口在执行完毕后立刻关闭,你根本看不到报错内容。
我的标准排查方式是:先别双击运行,在 PowerShell 里切换到脚本所在目录,然后手动执行:
.\your_script.ps1或者对 .bat 文件:
cmd /k your_script.bat这样窗口在执行完毕后保留,所有输出和报错信息都留在眼前。等排完错,再把前两行改为:
@echo off setlocal enabledelayedexpansion如果你想让脚本无论执行成功还是失败都保留窗口,可以在 .bat 最后一行加pause。但生产环境下我不建议保留,因为交互式 pause 会把脚本卡在无人值守的 CI 环境里。
还有一种闪退原因是脚本以管理员身份启动时路径变了。Windows 下 UAC 提权会让当前工作目录重置为 system32,导致相对路径引用全部失效,脚本里的日志文件写不进去,程序直接报错退出。解法是脚本开头主动cd /d %~dp0,把工作目录切到脚本自身所在目录。
4.3 游戏脚本与浏览器注入脚本的稳定性
另一个高频问题出自游戏脚本和浏览器注入脚本领域,热词里的"游戏页注入脚本太大游戏页面打不开"就是典型。
浏览器注入脚本(用户脚本)是通过油猴等插件注入到页面中执行的 JavaScript。页面打不开的原因通常是脚本性能问题:脚本体积过大、事件监听器嵌套过深、对页面 DOM 做了过于频繁的操作,导致浏览器主线程卡死。
这种情况下最简单粗暴的排查方法是用无痕模式加禁用其他插件的方式,确认是否是脚本互斥。然后打开浏览器自带的任务管理器(Chrome 按 Shift+Esc),看该标签页的 CPU 和内存占用。如果占用异常高,基本可以确定脚本本身存在性能问题。优化方向是:减少 DOM 查询次数,缓存选择器结果;用防抖函数处理高频事件;减少使用过于复杂的选择器。
游戏脚本我是这么看的:纯粹因为自动化工具把操作无限重复、破坏游戏公平性的脚本,我不做也不推荐做,这类东西不仅可能违反游戏条款,也会影响其他玩家的体验。但如果是一些辅助类、无障碍类的自动化工具,比如定时重启、单机游戏内辅助操作,那就是个人的技术实践,没什么问题。总的原则是:脚本不能以破坏他人体验为代价。
4.4 Python 脚本传参与接口自动化的常见坑
热词里有"python给另一个py脚本传递参数"和"java接口自动化测试框架",我各提一个高频问题。
Python 脚本传参,新手最容易犯的错是用sys.argv硬编码下标,比如固定取sys.argv[1]。一旦参数顺序变了或者漏传一个,脚本直接 IndexError。正确做法是使用argparse,定义参数名、类型、默认值,自动生成帮助信息,对漏传参数也能给出友好提示:
import argparse parser = argparse.ArgumentParser(description="部署脚本") parser.add_argument("--env", required=True, choices=["dev", "prod"], help="环境") parser.add_argument("--version", default="latest", help="版本号") args = parser.parse_args() print(f"正在部署 {args.version} 到 {args.env} 环境")这样就算参数传错,至少不会在没有任何提示的情况下崩溃。
Java 接口自动化测试框架,我接触过基于 TestNG + RestAssured 的组合。Java 的优势是性能强、类型约束严格、适合大型项目长期维护;缺点是写起来确实比 Python 啰嗦。如果团队对性能没有极致要求,我更倾向推荐 Python,开发效率差一个量级不止。但如果你们的技术栈统一是 Java,那 TestNG 的@DataProvider数据驱动和IAnnotationTransformer动态过滤用例很有价值,值得花时间研究。
5. 迈向自动化工程:AI 辅助、工具链整合与长期维护
脚本写多了,你会发现量变引起质变:从"解决一个问题"变成"解决一类问题"。这个阶段开始,自动化的关注点不再是某个脚本能不能跑,而是整个流程怎么管起来。
5.1 从单脚本到工作流:AI 办公自动化与工具链
"AI+自动化办公"是近两年的高频词。我的实际体会是,AI 在自动化里的角色分两种:一种是作为代码生成器,你描述需求,它输出脚本框架;另一种是作为自动化流程中的处理引擎,比如用大模型 API 读取非结构化文档,转成结构化数据结构,再喂给后续流程。
举例:我以前做政府采购标书文件的批量整理,几百份 PDF 里要抽取关键字段(公司名、价格、交付时间)。纯文本处理写正则的话,每种格式都要单独适配,痛苦程度极高。后来用 PDF 解析加 LLM 接口,把 PDF 文本传入提示词,让它返回 JSON 格式字段,准确率如果能到 90%,剩下的 10% 人工复核,整体效率反而大幅提升。这个工作流现在基本全是脚本自动跑的。
但是注意,AI 生成脚本是有边界的,它生成的代码可以做脚手架,但绝不能盲目信任。核心业务逻辑、权限控制、异常处理,必须自己把关。尤其涉及金融、支付等场景,必须经过严格 review 才能上生产环境。
5.2 让脚本更健壮的工程化微习惯
脚本一旦要长期运行,有几个原则我是刻在脑子里的,分享出来:
第一是幂等性。一个脚本重复执行多次,产生的结果应该是一致的。比如初始化目录,mkdir -p比mkdir安全得多;写入配置项,优先确认内容是否已存在,避免重复插入。
第二是整个流程带日志。日志是排错的生命线。好的日志至少包含时间戳、操作对象、执行结果、耗时。用 Python 的 logging 模块而不是 print,因为 logging 可以分级输出,线上环境只输出 WARNING 及以上,Debug 信息留到排查问题时再打开。
第三是尽量使用任务调度工具管理定时任务。Windows 计划任务、Linux crontab 都行,不要用 while 循环加 sleep 硬扛。crontab 最大的好处是独立管理、查看日志、失败告警集成,而自己写的死循环一旦出 bug,轻则 MySQL 连接数爆掉,重则雪崩式故障。
第四是处理退出码。任何脚本在主流程结束前,都应该根据执行结果设置非零退出码,这样 CI 工具(Jenkins、GitHub Actions)才能正确识别构建失败,否则脚本执行失败结果却显示绿色,等于自动化系统形同虚设。
提示:脚本维护成本随着时间线性增长,所以脚本里的注释和文档永远不能省略。你以为三个月后还能看懂自己写的代码,实际上三个月后你连当初的设计意图都会怀疑。另外,脚本名要规范,避免"test2_final_v3.sh"这种名字出现在生产目录里。
5.3 自动化脚本的合规边界
聊了很多技术细节,最后必须提一嘴边界问题。
自动化脚本本身是中性工具,就像一把刀——厨师拿来切菜,是工具;拿来做危险的事,是问题。我自己接触过的自动化方向和边界大致是:
Web 自动化可以用于自动化回归测试、数据采集(注意遵守目标网站的服务条款和 robots.txt),但不能用于恶意刷量、攻击性渗透。
接口自动化可以做授权范围内的接口测试和监控,注意不要涉及越权访问和敏感数据获取。
网络设备自动化配置变更,变更是高风险操作,必须搭配配置回滚方案和审批流程,不能随便在生产设备上跑脚本。
游戏脚本原则上不建议做破坏公平性的自动操作脚本,尤其是涉及真金白银的竞技类游戏。
安全测试类脚本(比如热词里有"布尔盲注爆破python脚本"),我只想说:这类内容只适用于授权范围内的渗透测试,团队有明确授权合同和测试边界才能动手。未经授权的任何探测行为,本质上是违法行为,脚本写得再好也没有意义。我建议想看网络安全方向的朋友,多关注合法的靶场练习平台和 CTF 比赛,这既安全又能系统学到技术。
最后说一点个人经验。自动化与脚本这条路,真正能让你走远的不是会多少框架、背多少 API,而是解决问题的思路:接到一个重复性任务时,你能快速判断"这个能不能自动化""用什么方式自动化成本最低""自动化失败时怎么兜底"。三个问题过一遍,你的自动化方案基本就是成熟的。踩坑不可怕,随着踩过的坑越来越多,你脑子里会有越来越多的"这一部分一定要这样处理"的预判,这时候你就真正入门了。