1. 先给“自动化与脚本”画一张地图:不是一门技术,而是一套分工体系
我这些年接触过不少刚入行的朋友,一说“自动化与脚本”就皱眉——这词儿范围太大了,测试的在说pytest,运维的在说Shell,搞办公自动化的在聊影刀,写游戏的在研究Lua脚本,连做PLC的也说自己天天搞自动控制。其实大家说的都是自动化,但根本不在同一张地图上。
先把坐标系立起来。所谓的“自动化与脚本”,按场景基本可以分成四大赛道:
| 赛道 | 代表工具 | 核心目的 | 适合人群 |
|---|---|---|---|
| 测试自动化 | pytest、Appium、Playwright、JMeter | 让机器代替人反复验证功能 | 测试工程师、开发工程师 |
| 系统与运维脚本 | Shell、PowerShell、Python | 处理文件、批处理、定时任务 | 运维工程师、后端开发 |
| 桌面与RPA办公自动化 | 影刀、AutoHotkey、浏览器脚本 | 把重复的鼠标键盘操作变成机器人 | 运营、产品、普通办公族 |
| 硬件与边缘自动化 | PLC梯形图、嵌入式脚本 | 控制真实设备按逻辑运行 | 自动化工程师、制造业从业者 |
这张表不是拿来背的,是拿来定位的。你自己想想现在手头的痛点在哪条赛道里,再去学对应工具,效率会高很多。大多数人学自动化失败,不是不努力,是拿错了地图——想测接口的人去啃Shell,想处理表格的人去研究PLC,当然学不下去。
另一个容易忽略的点:脚本是“轻武器”,框架是“重装甲”。脚本解决一次性、临时性、小范围的问题;框架解决常态化、团队协作、大规模回归的问题。你跟别人说你“写了个脚本”,通常意味着几百行代码搞定一件小事;你说“搭了个框架”,意味着别人也能在你搭的底座上继续写用例。本文后面提到的所有工具,我都按这个逻辑去区分它们的定位,你自己选型时也先想清楚:我要解决的是“一次”的问题,还是“长期反复”的问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 测试自动化不是只有pytest:四条主流的适用边界与选型逻辑
热搜词里大面积出现pytest、Appium、JMeter、Playwright,这类工具我天天在项目里用,可以负责任地说:没有“最好的测试框架”,只有“当前场景下最不别扭的组合”。下面拆开讲。
2.1 pytest:单测与接口自动化的主力,但不是全能选手
pytest是Python生态里最主流的测试框架,它的核心价值就一句话:用最少的样板代码写出结构清晰的测试用例。一个最简单的用例长这样:
python复制def test_login_with_valid_credentials():
result = login("admin", "123456")
assert result["code"] == 200
你看,不用继承什么TestCase类,不用写setUp那一套,纯粹就是一个普通函数加assert。它为什么火?因为它把“测试代码”这件事彻底平民化了——写测试的门槛降到和写普通函数一样低,团队自然更愿意写。配合fixture机制,你可以在不同用例之间共享前置数据和清理逻辑;配合parametrize装饰器,你可以把十几组测试数据一次性跑完:
python复制@pytest.mark.parametrize("username,password,expected_code", [
("admin", "123456", 200),
("admin", "wrong", 401),
("", "123456", 400),
])
def test_login_variants(username, password, expected_code):
result = login(username, password)
assert result["code"] == expected_code
但请注意,pytest的主场是接口测试和单元测试,不是UI测试。你要是打算用pytest去点网页按钮、操作浏览器弹窗,那是在拿锤子拧螺丝。UI层的自动化有专门的工具(下面说),pytest更多是作为“测试管家”把所有用例组织起来跑。
2.2 Playwright:UI自动化的当前最优解之一
UI自动化的痛点从来不是“能不能定位元素”,而是“稳不稳、快不快、好调试”。Selenium统治这个领域很多年,但Playwright这两年几乎成了我身边团队的新标配。为什么?三个点:
- 自动等待机制。你不用在每次点击前sleep两秒,它会在元素可交互时自动继续执行。这一点省掉了大约一半的稳定性问题。
- 多浏览器统一API。Chromium、Firefox、WebKit共用一个接口,写一遍代码到处跑。
- 自带录制与调试工具。
playwright codegen命令会打开浏览器并录制你的操作,自动生成脚本代码。我自己教新人入门UI自动化都是用这个方式——先让工具帮我写第一版,然后在生成的代码基础上去改造。
一个最基础的例子:
python复制from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://example.com")
page.click("text=Login")
page.fill("#username", "admin")
page.fill("#password", "123456")
page.click("button[type=submit]")
page.wait_for_selector(".welcome-message")
browser.close()
注意看:全局没有一个time.sleep。这就是Playwright的哲学——不去“猜时间”,而是“等状态”。谁也不能保证网络慢的时候3秒就够,但“等到元素出现”这个逻辑永远是对的。
2.3 Appium:移动端自动化的老将,但环境配置比写脚本难十倍
Appium做手机App自动化测试,思路是把手机App里的控件暴露成类似Web元素的东西,然后你用WebDriver协议去操作它。这个方案本身没问题,问题是环境搭建太折磨人:需要装JDK、Android SDK、Appium Server,还要配置设备或模拟器,如果测iOS还得有台Mac外加Xcode。我见过太多人卡在第一步——环境装了两周,脚本写了一小时。
如果只是想跑通Android端的自动化,一个务实的建议是:先在模拟器上把整套流程跑通,再上真机。真机调试时注意形态各异的无权限弹窗、系统弹窗,这些弹窗会把控件焦点抢走,脚本就开始“指东打西”了。
2.4 JMeter:接口压测的入门锚点,录制HTTPS脚本时有三个坑
JMeter常被用来做接口自动化或压测。它最方便的是有GUI界面,点点就能加线程组、加HTTP请求、加断言。但到了录制HTTPS脚本这个环节,三个经典问题几乎人人都遇过:
- 证书没装。JMeter录制HTTPS需要先把自己生成的证书导入到系统信任库,否则浏览器会拦截到“不安全连接”。不是让JMeter走代理就行,证书这步跳过,后面全乱。
- 脚本里出现一堆静态资源请求。不是你手动录了页面上的JS、CSS请求,而是这些内容被当作独立请求录进去了。过滤掉它们才有干净的请求脚本。
- 请求乱序或丢失。录制过程中浏览器里开了多个标签页时,请求可能被串到同一个线程组里,打乱了真实业务链路。建议每个业务流程单独建线程组。
3. 脚本语言的选择:Python、Shell、PowerShell分别解决什么问题
热搜词里另一大类是“Linux脚本”“Windows脚本”“powershell开机自启脚本”“shell脚本入门”。这些都属于“让操作系统按你的想法干活”的范畴。但这个范畴里也有分工,选错会非常别扭。
3.1 Shell:Linux环境下的“胶水语言”
Shell脚本本质上是把一串Linux命令排队执行。它不是万能的,但在Linux环境里处理文件、批量操作、定时任务,它就是最优解,没有之一。比如统计一个目录下所有日志文件的单词数:
bash复制#!/bin/bash
total=0
for f in /var/log/myapp/*.log; do
count=$(wc -l < "$f")
echo "$f: $count 行"
total=$((total + count))
done
echo "合计: $total 行"
这脚本放在crontab里每天早上跑一次,就能自动盯日志规模。你可能会说Python也能干这事,没错,但Shell的优势在于:Linux服务器上不用装任何额外环境,系统自带Bash,也没有依赖管理问题。而Python脚本换台机器可能缺第三方库,先pip install一堆东西才能跑。
Shell的短板同样明显:语法太老了,各种空格敏感、引号转义让人崩溃。数组、字典等数据结构用起来又别扭。所以我的个人经验是:处理对象是“命令与文件系统”时用Shell,处理对象是“数据与复杂逻辑”时用Python。
有一类坑必须单独说:Shell脚本里的空格和引号。初学者最容易写错的是这个:
bash复制# 错误示范:for遍历的列表被空格拆散了
for file in *.log 2024-*.txt; do
...
done
# 正确做法:用数组
files=(*.log)
for file in "${files[@]}"; do
...
done
我见过太多线上脚本因为文件名带空格而执行失败的案例,所以建议所有路径引用全部包上双引号(如"$file"),这已经是运维圈子里的肌肉记忆了。
3.2 PowerShell:Windows世界的救星,别拿它当“高级CMD”
很多Windows用户对命令行有阴影,觉得CMD的批处理脚本难懂又难查错。PowerShell的定位其实是为了根治这个局面——它不仅是一门脚本语言,更是一个完整的面向对象的自动化管理平台。注意“面向对象”这个词,它跟Shell最大的区别就在这:Shell处理的是文本流,PowerShell处理的是对象。
比如你想列出所有占用超过1GB内存的进程:
powershell复制Get-Process | Where-Object { $_.WorkingSet64 -gt 1GB } | Sort-Object WorkingSet64 -Descending
用CMD的话你得解析tasklist的输出文本,然后用findstr去过滤字符串,弱爆了。PowerShell里整个过程就是链式管道,每个对象都有属性,筛选、排序、导出一气呵成。
PowerShell开机自启脚本也算热搜词常客了。实现方式有几种,但我想提醒一点:放在启动文件夹里的脚本最容易实现,但在用户登录后才生效;要早于登录就执行的话得用计划任务。计划任务的PowerShell写法:
powershell复制$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\startup.ps1"
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName "MyStartupScript" -Action $action -Trigger $trigger
这里有个关键参数:-ExecutionPolicy Bypass。Windows默认禁止不经签名的PowerShell脚本执行,不加这个参数,你的脚本可能直接被杀。很多人说“脚本双击没反应”,十有八九是这个原因。
3.3 Python:跨平台自动化的“万能胶水”
如果你不确定该学哪个,我建议从Python入手。它不需要在某种特定操作系统下才能发挥,从文件整理、数据处理、爬虫到测试框架,乃至后面要说的RPA和硬件串口通信,Python都能干。一个核心优势是生态:几乎任何自动化需求,都能在pip仓库里找到对应的库。
比如自动化办公的经典需求——批量处理Excel:openpyxl 帮你读写xlsx文件;处理PDF用PyPDF2或pdfplumber;发邮件用smtplib;批量重命名文件用标准库os加shutil就行。
但Python也有自己的坑,最大的那个被热搜词精准命中——“pip无法将pip项识别为cmdlet”。我换个角度把这事说透:这个报错的本质是Windows的PATH环境变量里没有pip的路径。pip的安装路径通常是C:\Users\你的用户名\AppData\Local\Programs\Python\Python310\Scripts\。解决思路有两种:
- 简单方案:以后在终端里用
python -m pip代替pip。这样无论PATH怎么配,只要python本身能运行,就能带上pip模块。 - 根治方案:手动把Scripts目录加入系统环境变量PATH,然后重开终端。
第二种方案一次配好,后续装库都顺畅。顺便说一句,把python配置好了之后,再用python -m pip install xxx安装依赖时如果遇到网络慢的问题,建议配上国内镜像源,这也是老生常谈但很实用的一招。
4. 从零到一:写一个“下载文件自动归档”脚本的完整过程
光讲工具不练手都是空的。我用一个日常高频场景做完整演示——自动监控“下载”文件夹,发现新文件就按扩展名归档到对应子目录。这需求放在办公场景里非常典型:太多人的桌面和下载文件夹永远一片混乱。写成Python脚本,逻辑是这样:
python复制import os
import shutil
import time
from pathlib import Path
DOWNLOAD_DIR = Path.home() / "Downloads"
ARCHIVE_ROOT = Path.home() / "Archive"
CATEGORY_MAP = {
"图片": [".jpg", ".jpeg", ".png", ".gif", ".webp"],
"文档": [".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"],
"压缩包": [".zip", ".rar", ".7z", ".tar", ".gz"],
"安装包": [".exe", ".msi", ".dmg"],
"代码": [".py", ".js", ".ts", ".java", ".go", ".c", ".cpp", ".html"],
}
def archive_existing_files():
for file_path in DOWNLOAD_DIR.iterdir():
if not file_path.is_file():
continue
ext = file_path.suffix.lower()
for category, extensions in CATEGORY_MAP.items():
if ext in extensions:
target_dir = ARCHIVE_ROOT / category
target_dir.mkdir(parents=True, exist_ok=True)
dest = target_dir / file_path.name
# 处理重名文件:加序号
counter = 1
while dest.exists():
dest = target_dir / f"{file_path.stem}_{counter}{file_path.suffix}"
counter += 1
shutil.move(str(file_path), str(dest))
print(f"[移动] {file_path.name} -> {category}")
break
if __name__ == "__main__":
archive_existing_files()
这段代码不难,但里面有经验沉淀。先说重名处理——下载文件重名是高概率事件(比如“图片”文件夹里已经有一张photo.jpg,又来了一张同名的)。直接覆盖是灾难,所以我用_1、_2这种后缀递增方案。
再扩展成实时监控版本。要让脚本“盯”新文件,可以在主循环里不断检查,也可以用成熟的第三方库watchdog监听文件系统的创建事件。为了不把博文搞得太长,这里我展示用watchdog的监听日志思路:
python复制from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class DownloadHandler(FileSystemEventHandler):
def on_created(self, event):
if not event.is_directory:
print(f"发现新文件: {event.src_path}")
# 在这里调用归档逻辑
observer = Observer()
observer.schedule(DownloadHandler(), str(DOWNLOAD_DIR), recursive=False)
observer.start()
用监听事件而不是轮询的好处有两个:一是实时性强,文件一落盘就触发;二是省资源,轮询每5秒扫一遍目录很浪费,事件驱动只有真正发生变化时才干活。这是自动化脚本设计的核心思想——能用事件驱动的,别用轮询。
这个脚本的进阶方向是配合操作系统启动。Windows下可以用计划任务,Linux下用crontab或systemd timer。我个人的推荐是:Windows的“任务计划程序”比开机自启文件夹更稳,因为它可以选择“不管用户是否登录都要运行”;Linux下优先用systemd timer而不是crontab,因为systemd能记录日志、控制依赖,排查问题方便得多。
写完这个示例你会发现:自动化的第一价值不是“省时间”,而是杜绝遗忘。人可能哪天犯懒不去清理,脚本不会。这也是为什么说自动化是“过程纪律”,而不仅仅是“效率工具”。
5. 脚本稳定性的三道坎:环境、权限与“人类操作习惯”
脚本写出来能跑一次不难,难的是长期稳定运行。我经手的自动化项目,尤其是那些挂在后台跑的任务,最终暴露的问题几乎都集中在这三个方面。
5.1 环境问题比逻辑问题更容易让你崩溃
热搜词里那个“pip无法识别”的报错,本质就是环境问题。类似的还有Python脚本在A机器跑得好好的,换到B机器就ModuleNotFoundError——大概率是A机器是全局装库,B机器用了虚拟环境。这些环境坑的特点是:报错信息直指“某个模块不存在”,但你查逻辑完全没问题。
我的做法是:所有Python项目一律用虚拟环境(.venv),并在根目录放一个requirements.txt。新机器上一条命令pip install -r requirements.txt就能复原环境。然后再考虑用什么方式长期执行。虚拟环境的另外一个好处是不污染系统Python,避免“装了A库把B库搞崩”的连锁反应。
Shell脚本的环境问题则在于解释器路径。很多人写#!/bin/bash,但有些精简版Linux发行版里bash的位置不是标准路径。更保险的做法是用#!/usr/bin/env bash,让系统去PATH里找解释器。同理,Python脚本建议#!/usr/bin/env python3,这样能兼容不同的Python安装路径。
5.2 权限永远比你想的复杂
Windows下PowerShell脚本一闪而过、Linux下bash脚本提示Permission denied,这类问题在热搜里出现频率极高。它们都指向同一件事:脚本没有获得执行权限。
Linux解决方式:
bash复制chmod +x myscript.sh
Windows解决方式有两种,第一种是右键“以管理员身份运行”,第二种是执行策略设置——Set-ExecutionPolicy RemoteSigned。注意第二种涉及安全策略,我只建议在明确需要的前提下调整。如果你没有充分的编程验证经验,建议先采用“以管理员身份运行”的方式绕过。
还有一类容易忽略的权限问题是非交互式运行。计划任务里跑的脚本,运行账户可能跟你平时是不同的。此时它访问网络路径、映射驱动器时会有各种权限异常。排查这类问题的心法就一句话:看任务计划里“运行身份”到底是哪个账户。
5.3 脚本也要考虑“人类操作习惯”
这句话我拎出来单说,因为太容易被忽略了。脚本是给机器执行的,但它往往也要和人打交道。
比如你在脚本里弹出了一个对话框等待人工确认,那就得考虑人可能不在电脑前。超时无人操作怎么办?至少要有一个默认超时回退逻辑。
再比如脚本运行中产生日志,日志是给人看的。那就别只打“成功”“失败”,要打印出当时的上下文。我见过太多凌晨三点执行失败的脚本,日志里只有一行Error——排查的人看着那行字,内心是绝望的。好的日志要能还原现场:
python复制import logging
logging.basicConfig(
filename="/var/log/my_script.log",
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s"
)
logging.info("开始处理文件: %s", file_name)
什么时候该写日志,什么时候该报警,这本身也是一门课。我的简单标准是:临时脚本不写日志,长期任务必须有日志;失败报警要指向“怎么修”,而不只是“出错了”。
6. 设备老化测试的迷思:自动化脚本如何才能真正代表“连续运行”
热搜里有一条“设备老化测试全自动执行脚本”,挺吭的。这事的全貌其实是:设备老化测试依赖脚本持续循环执行同一套操作,积累运行时长、捕捉偶发故障。这里最大的诱惑是“写个持续死循环不就完了吗”——但这里面设计决策至关重要。
6.1 循环不等于老化
一个常见的错误是做成简单的持续循环调用。比如测一个硬件接口:
python复制while True:
device.exchange_data()
这个写法有三个隐患:
- 无法掌控“什么时候出问题”的上下文。一旦报错,你只知道设备挂了,不知道它是跑了100次还是5000次之后挂的。
- 单点异常直接终止脚本。如果是环境波动导致的偶发超时,它会终止整个老化进程,后续你再启动,又从头计数,测试数据失去连贯性。
- 没有足够的“思考时间”模拟真实使用节奏。老化不是全速压测,而应贴近真实使用的“操作-停顿-操作”节奏。
合理的版本至少要带计数器、日志和异常恢复:
python复制count = 0
while count < 10000:
try:
device.exchange_data()
count += 1
if count % 100 == 0:
log(f"已完成 {count} 次")
except DeviceTimeout:
log_warning(f"第 {count} 次超时")
device.recover()
except DeviceCrash:
log_error(f"第 {count} 次设备崩溃")
notify_admin()
break
6.2 老化测试脚本“多地记录”是底线
硬件设备一旦死机,脚本日志很可能同时丢失。我在实际项目里踩过这个坑:设备挂掉时,写日志的程序也没机会把缓存落盘。后来学乖了,每一步关键操作都立即写日志,而不是攒一批再写。同时把日志写两份——一份本地文件,一份通过网络送到另一台机器上。设备挂了,另一台机器的日志还在,就能还原“到底发生了什么”。
另外在老化测试里建议加上“心跳”机制:脚本每个循环向监控端发一个简单的TCP包,告诉对方“我还活着”。如果监控端在预期时间内没收到心跳,就直接判断测试异常了。这比脚本内部发了崩溃邮件再等更可靠——因为脚本可能没发出来就挂了。
这套思路也适用于其它长期运行的自动化任务。在我看来,所谓“设备老化测试全自动执行脚本”的最高秘诀,真不是脚本本身写得多巧,而是崩溃会话的捕获、记录和上报设计。代码是人写的,机器总会出意外,自动化的价值是让意外发生后可以迅速定位,而不是假装意外不会发生。
7. 说在最后:自动化的学习路径,从“复制别人的脚本”开始没有错
看到这里,你可能会觉得东西太多,不知道从哪下手。我给你一条务实的路径:
第一步,找到一个天天让你烦的重复动作,越简单越好。先别管用什么高级框架,打开终端,用最笨的方式把它写出来。第二步,把脚本里的关键参数提出来放到最顶部,加注释,让未来的你看得懂。第三步,让它在后台跑一个礼拜,再根据日志去修改。一个脚本稳定跑一周的成就感,比看十篇教程都强。
我个人特别想说的一点是:不要怕抄袭别人的脚本。GitHub上、论坛里、博客中,无数现成的脚本代码。把别人写好的东西跑通、读懂、改一改满足自己的需求,这是最正常的学习路径。真正把技术功底拉开差距的,是“跑通之后你是否愿意去读它的报错、去看它的文档、去了解它为什么这么写”。
我踩过很多坑的开始方式都是跑了别人的脚本,被报错虐得死去活来,然后被迫去查环境、查依赖、查API,最后才变成现在的熟练度。你今天为了“pytest报错找不到模块”或者“PowerShell执行策略”查了三十个帖子,明天在别的项目里再遇到类似问题,就完全不用查了。这行没有捷径,只有“跑得够多、查得够深”。把第一个自动化脚本跑到稳定运行,你就真正进门了。
