pgrep 命令详解与参数实测(Ubuntu 22.04)
pgrep 是按模式查找进程的利器:本文在 Ubuntu 22.04(procps-ng 3.3.17)逐参数实测,覆盖默认进程名匹配与 -f 全命令行匹配的本质差异、-l/-a 输出粒度、-u/-c 用户过滤与计数、-g/-G 进程组与真实组、-n/-o 最新最旧、-x 精确整名、-d 分隔符、-P 按父进程、-t 按终端、rc=1 语义与自身排除特性,并给出 kill 组合批删与按父进程清理等实战。
pgrep 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 pgrep 的每一个常用参数——默认进程名匹配与
-f全命令行匹配的本质差异、-l/-a两种输出粒度、-u/-c用户过滤与计数、-g/-G进程组与真实组、-n/-o最新最旧、-x精确整名、-d自定义分隔符、-P按父进程、-t按终端、无匹配时的 rc=1、以及"pgrep 自身永不入结果"的特性,全部命令与回显已在 Ubuntu 22.04(procps-ng 3.3.17)实测通过。
一、环境信息
本节记录实测环境与演示数据,便于读者对照复现。pgrep 是 procps 软件包的"按模式找进程"工具,与 ps、kill、pkill 构成进程管理工具链——它只查不杀,把 PID 列表交给调用方使用。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- pgrep 版本:procps-ng 3.3.17(见 2.3 实测)
- Shell:GNU bash 5.1.16(1)-release,root 身份执行
- 演示目录:
/root/demo/pgrep/ - 测试用户:普通用户
tester(uid=1012,用于-u/-G实验或多用户过滤场景) - 演示进程:
sleep 300(root 3 个)、sleep 120(root 1 个)、tester 用户的sleep 300/sleep 120、以及一个改名副本进程myworker(cp /usr/bin/sleep /root/demo/pgrep/myworker后运行,避免与系统同名进程混淆)
二、命令概览
2.1 语法与测试数据准备
pgrep 的语法是 pgrep [options] <pattern>:按给定扩展正则(ERE)匹配进程,默认只匹配进程名(内核 comm 值,最长 15 字符),输出匹配到的 PID;加上参数可以改为匹配全命令行、过滤用户/终端/父进程等,或改变输出格式。它天生是给 Shell 组合命令用的:kill $(pgrep -f 模式)、pid=$(pgrep -o 服务名) 都是最常见的用法。
💡 技术拓展:「按名找到进程 PID」不止 pgrep 一条路,常用方式横向对比——
方式 匹配依据 精确度 适用场景 pgrep 模式 扩展正则匹配进程名( -f匹配全命令行)可控( -x全等、-f全串)脚本取 PID、按多维度过滤 pidof 名称 按完整程序名全等匹配 高(不接受正则) 已知确切程序名快速取全部 PID ps -ef | grep 名称 人工 grep 命令行 低(易误杀 grep 自身,需 [x]技巧)临时人工排查 killall 名称 按名直接发信号( -s仅查询)中(默认精确匹配进程名) 一条命令杀同名进程 本文主角 pgrep 的差异化价值在「正则 + 过滤维度(用户/终端/父进程/运行态)」的组合能力,是脚本化进程管理的首选。
准备演示数据:起一个改名副本 myworker、root 下的 4 个 sleep、以及经 su 降权运行的 tester 进程(su tester -c 'sleep 300' & 前台等待模式,保证被杀后可正常回收):
cp -f /usr/bin/sleep /root/demo/pgrep/myworker
/root/demo/pgrep/myworker 300 &
sleep 300 & sleep 300 & sleep 300 & sleep 120 &
su tester -c 'sleep 300' & SU1=$!
su tester -c 'sleep 120' & SU2=$!
用 ps 对账,确认全部演示进程就位(这份 ps 快照是后文所有 pgrep 实验的"标准答案"):
root@ubuntu2204:~# ps -eo pid,user,comm,args | grep -E '[s]leep (300|120)|[m]yworker'
18 root myworker /root/demo/pgrep/myworker 300
19 root sleep sleep 300
20 root sleep sleep 300
21 root sleep sleep 300
22 root sleep sleep 120
23 root su su tester -c sleep 300
24 root su su tester -c sleep 120
(tester 降权后的 sleep 300/sleep 120 稍后由 -u tester 直接演示,PID 28/29。)
💡 说明:su 以
su tester -c '命令'方式运行时,su 进程本身仍在(它作为父进程等待目标命令),命令行里带着 "sleep 300" 字样——这正是后文-f参数教学的绝佳素材:按全命令行匹配时,包装进程也会被命中。
2.2 --help 全量输出(逐行照抄)
root@ubuntu2204:~# pgrep --help
Usage:
pgrep [options] <pattern>
Options:
-d, --delimiter <string> specify output delimiter
-l, --list-name list PID and process name
-a, --list-full list PID and full command line
-v, --inverse negates the matching
-w, --lightweight list all TID
-c, --count count of matching processes
-f, --full use full process name to match
-g, --pgroup <PGID,...> match listed process group IDs
-G, --group <GID,...> match real group IDs
-i, --ignore-case match case insensitively
-n, --newest select most recently started
-o, --oldest select least recently started
-O, --older <seconds> select where older than seconds
-P, --parent <PPID,...> match only child processes of the given parent
-s, --session <SID,...> match session IDs
-t, --terminal <tty,...> match by controlling terminal
-u, --euid <ID,...> match by effective IDs
-U, --uid <ID,...> match by real IDs
-x, --exact match exactly with the command name
-F, --pidfile <file> read PIDs from file
-L, --logpidfile fail if PID file is not locked
-r, --runstates <state> match runstates [D,S,Z,...]
--ns <PID> match the processes that belong to the same
namespace as <pid>
--nslist <ns,...> list which namespaces will be considered for
the --ns option.
Available namespaces: ipc, mnt, net, pid, user, uts
-h, --help display this help and exit
-V, --version output version information and exit
For more details see pgrep(1).
帮助把选项分成两组逻辑:上面是输出控制(-d/-l/-a/-v/-w/-c),中间是匹配过滤(-f/-g/-G/-i/-n/-o/-O/-P/-s/-t/-u/-U/-x),末尾是辅助(pidfile、namespace)。本文逐一实测最常用的 12 项。
2.3 版本信息
root@ubuntu2204:~# pgrep --version | head -2
pgrep from procps-ng 3.3.17
三、参数逐项实测
3.1 默认匹配语义:进程名上的正则部分匹配
不带选项的 pgrep 模式 在**进程名(comm)**上做 ERE 部分匹配,输出命中的 PID:
root@ubuntu2204:~# pgrep myworker; echo rc=$?
18
rc=0
部分匹配意味着 mywor 也能命中 myworker(模式是正则而非完整串,未锚定):
root@ubuntu2204:~# pgrep mywor -l; echo partial_rc=$?
18 myworker
partial_rc=0
注意是部分匹配、不是子串匹配:模式作为 ERE 可以写 my.*er、mywor(k|X) 这类正则;想要完整名等值匹配必须用 -x(见 3.7)。
3.2 -l 列出进程名 / -a 列出全命令行
默认输出只有 PID,排查时往往不够。-l 在 PID 后附进程名,-a 附完整命令行(含参数路径):
root@ubuntu2204:~# pgrep -l myworker
18 myworker
root@ubuntu2204:~# pgrep -a myworker
18 /root/demo/pgrep/myworker 300
对照 2.1 的 ps 快照可见:-l 显示的是 comm(myworker),-a 显示的是 args(/root/demo/pgrep/myworker 300)——当同一个二进制被复制成多个名字、或用参数区分实例时,-a 的信息量远大于 -l。再看 tester 用户的进程(多用户环境下的对账场景):
root@ubuntu2204:~# pgrep -u tester -a sleep
28 sleep 300
29 sleep 120
3.3 -f 全命令行匹配:与默认行为的本质差异
这是 pgrep 最容易踩坑的参数。默认只在进程名上匹配,因此把含空格的完整命令 "sleep 300" 当模式时,进程名里根本不存在这个字符串,返回 rc=1 空结果:
root@ubuntu2204:~# pgrep 'sleep 300' -l; echo rc=$?
rc=1
加 -f 后改为匹配全命令行(/proc/PID/cmdline),一切就通了:
root@ubuntu2204:~# pgrep -f 'sleep 300' -l; echo rc=$?
19 sleep
20 sleep
21 sleep
23 su
28 sleep
rc=0
逐行解读这个"惊喜"结果:19/20/21 是 root 的 sleep 300,28 是 tester 的 sleep 300,而 23 是 su 包装进程——它的命令行 su tester -c sleep 300 恰好包含 "sleep 300" 字样,被 -f 一起命中。这正是 -f 的双刃剑特性:匹配面宽了,但也容易误伤包装脚本/父命令。用 ^...$ 锚定收窄:
root@ubuntu2204:~# pgrep -f '^sleep 300$' -l; echo rc=$?
19 sleep
20 sleep
21 sleep
28 sleep
rc=0
-f 的模式同样是正则部分匹配——sleep 1 也能命中 sleep 120(1 是 12 的前缀):
root@ubuntu2204:~# pgrep -f 'sleep 1' -l; echo rc=$?
22 sleep
24 su
29 sleep
rc=0
💡 要点:
pgrep 名字查 comm,pgrep -f 片段查整条命令行;用-f时模式要尽量带锚定或独特字符串,避免sleep 1命中sleep 120式的前缀误伤。
3.4 -u 按用户过滤 / -c 计数
-u 按有效 UID(用户名或数字均可)过滤;与名字/-f 组合即为"查某用户的某进程":
root@ubuntu2204:~# pgrep -u tester -l sleep
28 sleep
29 sleep
root@ubuntu2204:~# pgrep -u root -f 'sleep 300' -l; echo rc=$?
19 sleep
20 sleep
21 sleep
23 su
rc=0
-c 把输出从 PID 列表换成计数,适合写监控/巡检脚本:
root@ubuntu2204:~# pgrep -u tester -c sleep; echo rc=$?
2
rc=0
root@ubuntu2204:~# pgrep -c -f 'sleep (300|120)'; echo rc=$?
8
rc=0
计数 8 的构成:root 的 sleep 300 ×3 + sleep 120 ×1 + tester 的 2 个 sleep + 2 个 su 包装进程——-f 匹配下 su 也计入,印证 3.3 的语义。
3.5 -g 按进程组 / -G 按真实组
-g 匹配进程组 ID(PGID),-G 匹配真实组 ID(GID),二者极易混淆:前者是作业控制概念,后者是用户组概念。-g 0 表示"我所在的当前进程组",把演示脚本组内的进程一网打尽(含新起的 sleep 300,PID 41):
root@ubuntu2204:~# sleep 300 & pgrep -g 0 -l; echo rc=$?
7 bash
18 myworker
19 sleep
20 sleep
21 sleep
22 sleep
23 su
24 su
41 sleep
rc=0
-G 0(root 组)+ -f 'sleep 300' 组合过滤,tester 的进程(GID 1016)被排除:
root@ubuntu2204:~# pgrep -G 0 -f 'sleep 300' -l; echo rc=$?
19 sleep
20 sleep
21 sleep
23 su
41 sleep
rc=0
root@ubuntu2204:~# TGID=$(id -g tester); pgrep -G $TGID -f 'sleep (300|120)' -l; echo rc=$?
28 sleep
29 sleep
rc=0
3.6 -n 最新 / -o 最旧
同一模式命中多个进程时,-n 只输出最新启动的,-o 只输出最旧的(各一个 PID):
root@ubuntu2204:~# pgrep -n -l -f 'sleep 300'; echo rc=$?
41 sleep
rc=0
root@ubuntu2204:~# pgrep -o -l -f 'sleep 300'; echo rc=$?
19 sleep
rc=0
对照 2.1 快照:41 是 3.5 实验中新起的(最新),19 是最早起的(最旧)。
3.7 -x 精确整名匹配
-x 把模式从"正则部分匹配"变成"完整名等值匹配"。短模式 mywor 在默认语义下命中、在 -x 下不命中:
root@ubuntu2204:~# pgrep -x mywor -l; echo exact_short_rc=$?
exact_short_rc=1
root@ubuntu2204:~# pgrep -x myworker -l; echo exact_full_rc=$?
18 myworker
exact_full_rc=0
💡 要点:
pgrep 名字在生产脚本里有前缀误伤风险(如pgrep ssh同时命中sshd与ssh-agent的名字场合),对需要"全名等值"的判断一律加-x。
3.8 -d 自定义分隔符
默认 PID 每行一个;-d 指定分隔符后单行输出,方便直接塞进变量或日志:
root@ubuntu2204:~# pgrep -d ',' -f 'sleep 300'; echo rc=$?
19,20,21,23,28,41
rc=0
3.9 -P 按父进程过滤(含实战:清理某 Shell 的全部子进程)
-P 只匹配指定 PPID 的直接子进程。清洁环境下脚本 Shell(PID 7)起两个 sleep 后,pgrep -P $$ -l 精确列出这两个子进程:
root@ubuntu2204:~# sleep 300 & sleep 300 & echo shell_pid=$$; pgrep -P $$ -l; echo rc=$?
shell_pid=7
13 sleep
14 sleep
rc=0
反向应用即"清理某父进程的所有子进程"——kill $(pgrep -P $$),随后 -P 查询返回 rc=1 确认无残留:
root@ubuntu2204:~# kill $(pgrep -P $$) 2>/dev/null; sleep 0.3; pgrep -P $$ -l || echo no-children-left
no-children-left
3.10 -t 按终端过滤
-t 按控制终端过滤,终端名可写全路径或缩写(pts/0)。用 script 构造一个带伪终端的会话:script 在 pty 里跑 sleep 300,其内部 shell(sh)与 sleep 都挂在 pts/0 上:
root@ubuntu2204:~# script -qfc 'sleep 300' /dev/null & SCD=$!; sleep 0.7; CHILD=$(pgrep -P $SCD | head -1); TTY=$(ps -o tty= -p $CHILD | tr -d ' '); echo script_pid=$SCD child=$CHILD child_tty=$TTY
script_pid=53 child=55 child_tty=pts/0
root@ubuntu2204:~# ps -o pid,tty,args -p $CHILD
PID TT COMMAND
55 pts/0 sh -c sleep 300
按 pts/0 过滤,会话内的 sh 与 sleep 全部命中:
root@ubuntu2204:~# pgrep -t $TTY -l; echo rc=$?
55 sh
56 sleep
rc=0
配合 pkill -t 即可整段踢掉该终端会话(详见《pkill 命令详解与参数实测(Ubuntu 22.04)》3.6 节)。
3.11 无匹配行为:空输出 + rc=1
pgrep 找不到匹配时不输出任何内容、退出码 1——这是脚本分支判断的依据(&&/|| 恰好消费这个退出码):
root@ubuntu2204:~# pgrep zzz-not-exist-proc; echo rc=$?
rc=1
3.12 pgrep 自身永不入结果
ps | grep 组合有著名的"grep 自匹配"问题(要靠 [g]rep 技巧规避),pgrep 从设计上永远排除自己。起一个命令行含唯一标记的 tail 进程做目标:
root@ubuntu2204:~# echo marker > /root/demo/pgrep/marker-file; tail -n 0 -f /root/demo/pgrep/marker-file & MK=$!; echo marker_pid=$MK
marker_pid=70
root@ubuntu2204:~# pgrep -f marker-file; echo rc=$?
70
rc=0
pgrep 自己的命令行 pgrep -f marker-file 同样包含 marker-file,但结果里只有 70。把模式换成"查 pgrep 自己"——空结果 rc=1,自排除语义非常干净:
root@ubuntu2204:~# pgrep -f 'pgrep -f marker-file'; echo rc=$?
rc=1
root@ubuntu2204:~# kill $MK 2>/dev/null; echo marker-killed
marker-killed
四、实战示例
4.1 按模式批量终止:kill $(pgrep -f ...)
pgrep 最高频的组合用法。起 2 个 sleep 300 和 1 个 sleep 120,用锚定模式精确终止前两者:
root@ubuntu2204:~# sleep 300 & sleep 300 & sleep 120 & echo started
started
root@ubuntu2204:~# ps -eo pid,args | grep -E '[s]leep (300|120)'
86 sleep 300
87 sleep 300
88 sleep 120
root@ubuntu2204:~# kill $(pgrep -f '^sleep 300$') 2>/dev/null; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -c -f '^sleep (300|120)$'; echo rc=$?
1
rc=0
root@ubuntu2204:~# kill $(pgrep -f '^sleep 120$') 2>/dev/null; sleep 0.3; pgrep -c -f '^sleep (300|120)$' || echo all-cleaned
0
all-cleaned
💡 要点:
$(...)展开时 pgrep 的多行 PID 自动变成kill PID1 PID2;pgrep -c ... || echo是天然的"清干净了吗"断言。
4.2 按父进程清理:kill $(pgrep -P ...)
脚本收尾时清理自己启动的全部子进程,比记录每个 PID 更健壮(3.9 已实测核心命令,这里给出完整闭环):
root@ubuntu2204:~# sleep 300 & sleep 300 & echo shell_pid=$$; pgrep -P $$ -l
shell_pid=7
13 sleep
14 sleep
root@ubuntu2204:~# kill $(pgrep -P $$) 2>/dev/null; sleep 0.3; pgrep -P $$ -l || echo no-children-left
no-children-left
4.3 -a 定位"这个进程到底在跑什么"
接手陌生服务器时,先按关键字拿到完整命令行再决定动作。例如查 tester 用户 120 秒级的 sleep 实例:
root@ubuntu2204:~# pgrep -u tester -a sleep
28 sleep 300
29 sleep 120
root@ubuntu2204:~# pgrep -f 'sleep 1' -l
22 sleep
24 su
29 sleep
第一条用 -a 直接给出参数区分实例;第二条演示排查技巧——用短前缀 sleep 1 找出所有 1xx 秒的实例时,连 su 包装进程(24)都会浮出水面,能顺带发现"这个 sleep 是谁、以什么方式启动的"(su tester -c sleep 120),排查启动链路非常有用。
五、注意事项与常见问题
Q1:ps 明明能看到进程,pgrep 却匹配不上?
A:最常见原因是默认只匹配进程名(comm),而 comm 来自可执行文件名且最长 15 字符;进程若带参数启动(sleep 300 的名字是 sleep 而非 sleep 300),必须加 -f 匹配全命令行(3.3 实测)。
Q2:pgrep 'sleep 300' 为什么无结果?
A:同 Q1——模式被当成"进程名"整体去匹配,进程名里没有空格。改为 pgrep -f 'sleep 300'(3.3 实测 rc 从 1 变 0)。
Q3:用了 -f 后为什么会多出 su、bash 这类进程?
A:-f 匹配整条命令行,包装进程(su tester -c sleep 300)的命令行里带着目标字样,自然命中(3.3/3.4 实测 23/24 号 su)。写精确模式(^...$ 锚定)或用 -x 可避免误伤。
Q4:pgrep -l 显示的名字和 ps 不一致?
A:-l 输出 comm(15 字符截断的内核名),-a 输出完整 args。同名多实例、复制改名的二进制(本文的 myworker)场景下,用 -a 才能看到真实身份。
Q5:僵尸进程会被 pgrep 算进计数吗?
A:按名匹配(-l/-c)会——僵尸的 comm 仍是原名;按 -f 不会——僵尸的 cmdline 已清空。写巡检脚本计数时若发现"进程数比预期多",先用 ps 核对是否混入僵尸。
Q6:pgrep 会匹配到自己吗?
A:不会。procps 在实现中显式排除了自身 PID,即使模式就是它自己的命令行(3.12 实测 rc=1)。这是相对 ps | grep 的结构性优势。
Q7:pgrep -t 匹配不到进程?
A:-t 的参数是控制终端名(如 pts/0、tty1),写法必须与 ps 的 TT 列一致(可带 /dev/ 前缀);后台孤儿进程无控制终端,不属于任何 pts,自然查不到。构造带终端的会话可用 script(3.10 实测)。
六、参考链接
- pgrep(1) 手册(man7.org):https://man7.org/linux/man-pages/man1/pgrep.1.html
- procps-ng 项目主页:https://gitlab.com/procps-ng/procps
- Ubuntu 22.04 procps 软件包页:https://packages.ubuntu.com/jammy/procps
- proc(5)——/proc 文件系统(cmdline/comm 数据来源):https://man7.org/linux/man-pages/man5/proc.5.html
- 姊妹篇:《kill 命令详解与参数实测(Ubuntu 22.04)》《pkill 命令详解与参数实测(Ubuntu 22.04)》《killall 命令详解与参数实测(Ubuntu 22.04)》