Linux 下这种需求实在太常见了。很多时候我们解决服务异常的办法并不是体系化的,而是“想起来一个杀一个”,端口被占就 lsof 找 PID 再 kill,服务起不来就把看起来相关的进程全列为嫌疑犯。可一旦需求变成“把 ps 输出中所有包含 xx 字段的进程都杀掉”,事情就没那么简单了:你要匹配的 xx 到底是进程名、完整命令行、还是启动时的某个参数?批量 kill 怎么防止误杀同名的其他服务?为什么有时候 kill 完了进程还在?这篇文章我要把这个高频操作从头到尾拆干净,从 ps 字段定位、PID 提取到安全终止,每一步都给可以直接抄的写法,顺便把容易翻车的细节全部点出来。无论你是刚接触命令行的新手,还是已经踩过几次坑的运维开发,按这条路走都能少折腾一下午。
1. 先搞清楚:你想匹配的到底是 ps 里的哪一个“字段”
1.1 大多数人第一步就错了:只记得 ps aux
很多人一上来就是 ps aux | grep xxx,然后从输出里肉眼找 PID。这方法在进程少的时候没毛病,但一旦进程列表上百条,就会遇到一个很要命的问题:你根本不知道 grep 匹配到的是整行的哪个位置。
ps aux 和 ps -ef 的输出列名不一样,但本质上都是在读 /proc 目录下的进程快照。ps aux 是 BSD 风格,输出格式如下:
bash复制USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 168236 11472 ? Ss Mar10 0:47 /usr/lib/systemd/systemd
ps -ef 是 System V 风格:
bash复制UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Mar10 ? 00:00:47 /usr/lib/systemd/systemd
两者关键区别在于最后列:COMMAND 和 CMD。管你是 ps aux 还是 ps -ef,最后那列都是“完整启动命令行”。注意,这里有个隐藏坑:当输出到终端时,ps 会自动截断超长命令行,你看到的 CMD 可能只有一小段,真正干活的参数根本没显示出来。想看完整命令要用 ps auxww 或者 ps -efww,多加两个 w。写脚本做匹配时尤其要加,否则你以为字段没匹配到,实际只是显示被截断了。
1.2 pick 自定义 -o 输出,脚本才不靠猜
既然要在脚本里做字段匹配,与其对着不固定的列数猜,不如直接告诉 ps 你要哪些列。这是我的习惯用法:
bash复制ps -eo pid=,ppid=,user=,stat=,etime=,args=
-e表示所有进程,等价于-A-o后面是自定义输出字段,逗号分隔- 字段名后面跟着
=,作用是去掉表头行。处理结果时不用再tail -n +2,省一步 args就是完整命令行,和cmd、command是同一个东西
推荐在脚本里优先用这组字段:
| 字段名 | 含义 | 使用场景 |
|---|---|---|
| pid | 进程号 | kill 的对象 |
| ppid | 父进程号 | 排查父子关系、守护进程 |
| pgid | 进程组号 | 按进程组批量终止 |
| user | 启动用户 | 共享机器上确认归属 |
| comm | 可执行文件名 | 只看“程序叫什么” |
| args | 完整命令行 | 匹配 JAR 路径、启动参数 |
| stat | 进程状态 | 过滤僵尸进程、休眠进程 |
| etime | 已运行时长 | 判断是旧进程还是新进程 |
| lstart | 启动时间点 | 人工确认使用 |
