pkill 命令详解与参数实测(Ubuntu 22.04)

pkill 把查找与发信号合成一条命令:本文在 Ubuntu 22.04(procps-ng 3.3.17)逐参数实测,覆盖默认 TERM 实证、--signal/-9 等价写法、-u 按用户、-n/-o 只杀最新最旧、-e 回显所杀、-c 计数陷阱、-t 踢终端会话、-x 精确名与部分模式差异、-USR1 自定义信号送达,重点剖析 pkill bash 自杀风险与唯一标记安全替代方案。

Linux命令Linux系统UbuntuUbuntu 22.04命令行进程管理2026/10/15

pkill 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 pkill 的每一个常用参数——按进程名与 -f 全命令行两种匹配方式、默认 TERM 与 --signal/-9 显式信号、-u 按用户、-n 只杀最新、-t 踢终端会话、-x 精确整名、-USR1 自定义信号送达、部分模式与精确名的差异、无匹配 rc=1,以及"pkill bash 自杀风险"的安全替代方案,全部命令与回显已在 Ubuntu 22.04(procps-ng 3.3.17)实测通过。

一、环境信息

本节记录实测环境与演示数据,便于读者对照复现。pkill 与 pgrep 是同一软件包的孪生工具:匹配逻辑完全相同,差别只在动作——pgrep 打印 PID,pkill 直接向所有命中进程发信号。它把"查找 + 逐个 kill"两步合成一条命令,是批量管理进程的主力工具。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • pkill 版本:procps-ng 3.3.17(见 2.3 实测)
  • Shell:GNU bash 5.1.16(1)-release,root 身份执行
  • 演示目录:/root/demo/pkill/
  • 测试用户:普通用户 tester(uid=1012,用于 -u 按用户实验)
  • 演示进程:3 个改名副本 myworker(cp /usr/bin/sleep /root/demo/pkill/myworker)、root 的 sleep 300 ×3 与 sleep 120 ×1、tester 用户的 sleep 77/sleep 88、两个 trap 工作脚本与一个改名 bash mybash

二、命令概览

2.1 语法与测试数据准备

pkill 的语法是 pkill [options] <pattern>:按扩展正则(ERE)匹配进程并向每个命中进程发送信号,不指定信号时默认发 SIGTERM。匹配规则与 pgrep 一致:默认只匹配进程名(comm,最长 15 字符),-f 匹配全命令行;无匹配时退出码 1。默认情况下 pkill 不会匹配自己,但可能匹配到其他同名进程——这是 3.9 节安全实践的核心议题。

准备演示数据(多组同名、跨用户、带标记的进程,供各参数分别命中):

cp -f /usr/bin/sleep /root/demo/pkill/myworker
/root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 &
sleep 300 & sleep 300 & sleep 300 & sleep 120 &
su tester -c 'sleep 77' & SU1=$!
su tester -c 'sleep 88' & SU2=$!

用 ps 对账确认全部演示进程就位(tester 的两个 sleep 以 su 前台方式持有,被杀时可正常回收):

root@ubuntu2204:~# ps -eo pid,user,comm,args | grep -E '[s]leep (300|120|77|88)|[m]yworker'
    334 root     myworker        /root/demo/pkill/myworker 300
    335 root     myworker        /root/demo/pkill/myworker 300
    336 root     myworker        /root/demo/pkill/myworker 300
    337 root     sleep           sleep 300
    338 root     sleep           sleep 300
    339 root     sleep           sleep 300
    340 root     sleep           sleep 120
    341 root     su              su tester -c sleep 77
    342 root     su              su tester -c sleep 88
    344 tester   sleep           sleep 88
    345 tester   sleep           sleep 77

2.2 --help 全量输出(逐行照抄)

root@ubuntu2204:~# pkill --help

Usage:
 pkill [options] <pattern>

Options:
 -<sig>, --signal <sig>    signal to send (either number or name)
 -q, --queue <value>       integer value to be sent with the signal
 -e, --echo                display what is killed
 -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).

与 pgrep --help 对比:过滤选项几乎一一对应,头部多出 --signal/-q(信号指定与 sigqueue 队列值),少了输出类选项、多了 -e(回显所杀目标)。帮助末行 "see pgrep(1)" 也点明二者共用同一手册页。

2.3 版本信息

root@ubuntu2204:~# pkill --version | head -2
pkill from procps-ng 3.3.17

三、参数逐项实测

3.1 按进程名杀一组:-x 与默认匹配

一条命令终止全部同名进程——演示数据里的 3 个 myworker 被 pkill -x myworker 一次清空,验证计数归零:

root@ubuntu2204:~# pkill -x myworker; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -c -x myworker || echo all-myworker-dead
0
all-myworker-dead

💡 要点:这就是 pkill 与 kill 的本质区别——kill 只认 PID(一次一个或一组显式 PID),pkill 按模式匹配后逐个投递信号,天然适合"杀一批"。

3.2 默认信号 TERM 实证(trap worker)

pkill 不带信号参数时默认发什么?用 trap 工作脚本实证:worker 注册 TERM 处理器,pkill -f 模式 命中后,worker 打印捕获信息并优雅退出:

cat > /root/demo/pkill/trap-pkill.sh <<'EOF'
#!/bin/bash
trap 'echo "caught TERM from pkill, do cleanup, exit 0"; exit 0' TERM
echo "pkill-trap-worker pid=$$"
sleep 300 &
while :; do wait; done
EOF
root@ubuntu2204:~# bash /root/demo/pkill/trap-pkill.sh --pkill-trap-marker & WP=$!; sleep 0.5; pkill -f pkill-trap-marker; wait $WP 2>/dev/null; echo worker_rc=$?
pkill-trap-worker pid=352
caught TERM from pkill, do cleanup, exit 0
worker_rc=0

worker_rc=0:进程捕获 TERM、执行清理、主动退出。结论:pkill 默认信号是 SIGTERM,与 kill 默认行为一致。

3.3 -u 按用户杀

-u 按有效 UID 过滤目标——只杀 tester 用户的 sleep(344/345),root 的同名进程不受影响。注意输出中两行 Terminated:那是 su 前台会话检测到其子进程被终止后打印的会话消息,属正常回显:

root@ubuntu2204:~# pkill -u tester -x sleep; echo rc=$?
Terminated
Terminated
rc=0
root@ubuntu2204:~# kill $SU1 $SU2 2>/dev/null; sleep 1; pgrep -c -u tester -f 'sleep (77|88)' || echo tester-clean
tester-clean

清理后按 -f 计数验证 tester 名下已无目标进程(-f 匹配 cmdline,不会把僵尸误计入)。

3.4 -9 强杀与 --signal 等价写法

-9 与 --signal KILL 是两种等价的信号指定写法(帮助首行 -<sig>, --signal <sig> signal to send (either number or name))。锚定模式精确命中 root 的 sleep 300 ×3:

root@ubuntu2204:~# pkill -9 -f '^sleep 300$'; echo rc=$?
bash: line 3:   337 Killed                  sleep 300
bash: line 3:   338 Killed                  sleep 300
bash: line 3:   339 Killed                  sleep 300
rc=0

(三行 Killed 是外层 Shell 对被杀后台作业的终止通知,正是三个 sleep 300 阵亡的现场记录。)再验证长选项写法杀掉 sleep 120,最后确认全清:

root@ubuntu2204:~# pkill --signal KILL -f '^sleep 120$'; echo rc=$?
bash: line 3:   340 Killed                  sleep 120
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -c -f '^sleep (300|120)$' || echo all-root-sleep-dead
0
all-root-sleep-dead

3.5 -n 只杀最新 / -o 只杀最旧

当同名进程有多个实例时,-n 把打击面收窄到最新启动的那一个。重起 3 个 myworker(364/365/366),pkill -n -x myworker 后只少了最新的 366:

root@ubuntu2204:~# pkill -x myworker 2>/dev/null; /root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 & sleep 0.3; ps -eo pid,comm,args | grep '[m]yworker'
    364 myworker        /root/demo/pkill/myworker 300
    365 myworker        /root/demo/pkill/myworker 300
    366 myworker        /root/demo/pkill/myworker 300
root@ubuntu2204:~# pkill -n -x myworker; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.3; ps -eo pid,comm,args | grep '[m]yworker'
    364 myworker        /root/demo/pkill/myworker 300
    365 myworker        /root/demo/pkill/myworker 300

剩下 364/365 完好,再补一刀清空收尾:

root@ubuntu2204:~# pkill -x myworker; sleep 0.2; echo rest-killed
rest-killed

对称的 -o 命中最旧启动的那一个:起两个实例(58/59),pkill -o 后只剩最新的 59:

root@ubuntu2204:~# cp -f /usr/bin/sleep /root/demo/pkill/myworker; /root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 & sleep 0.3; ps -eo pid,comm,args | grep '[m]yworker'
     58 myworker        /root/demo/pkill/myworker 300
     59 myworker        /root/demo/pkill/myworker 300
root@ubuntu2204:~# pkill -o -x myworker; echo oldest_rc=$?
oldest_rc=0
root@ubuntu2204:~# ps -eo pid,comm,args | grep '[m]yworker'
     59 myworker        /root/demo/pkill/myworker 300
root@ubuntu2204:~# pkill -x myworker; sleep 0.2; echo cleaned
cleaned

💡 要点:-n 的典型场景是"只重启最新失控实例"(老实例还在服务流量);-o 适合"淘汰最老的陈旧实例"。二者都只投递给一个进程。

3.6 -e 回显所杀目标

pkill 默认静默——rc=0 只说明"投了信号",但杀了谁看不到。-e(echo)逐个打印被击杀目标,批量操作时是最好的事后审计记录:

root@ubuntu2204:~# cp -f /usr/bin/sleep /root/demo/pkill/myworker; /root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 & sleep 0.3; echo started
started
root@ubuntu2204:~# pkill -e -x myworker; echo echo_rc=$?
myworker killed (pid 35)
myworker killed (pid 36)
myworker killed (pid 37)
echo_rc=0
root@ubuntu2204:~# pgrep -c -x myworker || echo all-dead
0
all-dead

3.7 -c 计数(注意:照样发信号,不是预演)

pkill -c 把输出换成命中数量——但它仍然照常投递信号,只是把"逐个报 PID"(-e)换成"报总数"。实测:pkill -c -x myworker 输出 2,紧接着 pgrep -c 已归零:

root@ubuntu2204:~# /root/demo/pkill/myworker 300 & /root/demo/pkill/myworker 300 & sleep 0.3; echo two-started
two-started
root@ubuntu2204:~# pkill -c -x myworker; echo count_rc=$?
2
count_rc=0
root@ubuntu2204:~# pgrep -c -x myworker; echo alive_check_rc=$?
0
alive_check_rc=1

⚠️ 注意:想要只统计、不动手的安全预演,请用 pgrep -c 模式(pgrep 本就不发信号);pkill -c 的语义是"统计一下我杀了几个"。

3.8 -t 踢终端会话

-t 按控制终端过滤目标。用 script 构造一个带伪终端的会话(内部跑 sleep 300),确认会话挂在 pts/0 后,一条 pkill -t 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 session_pid=$SCD child=$CHILD tty=$TTY
session_pid=392 child=394 tty=pts/0
root@ubuntu2204:~# pkill -t $TTY; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.5; pgrep -t $TTY -l || echo pty-session-killed
pty-session-killed
root@ubuntu2204:~# pkill -f 'script -qfc' 2>/dev/null; echo script-cleaned
script-cleaned

pgrep -t 已查不到该终端上的进程,会话踢除成功。这正是管理员处理"僵死登录会话"的标准手法:先 who/w 看 pts 编号,再 pkill -t pts/N。

3.9 -x 精确名 vs 部分模式匹配

pkill 的匹配语义与 pgrep 完全一致:默认正则部分匹配,-x 整名等值。短模式 mywor 在 -x 下无匹配(rc=1,目标毫发无损),去掉 -x 立即命中并击杀:

root@ubuntu2204:~# /root/demo/pkill/myworker 300 & sleep 0.3; pkill -x mywor; echo exact_short_rc=$?
exact_short_rc=1
root@ubuntu2204:~# pgrep -x myworker >/dev/null && echo still-alive
still-alive
root@ubuntu2204:~# pkill mywor; echo partial_rc=$?
partial_rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -x myworker || echo killed-by-partial-pattern
killed-by-partial-pattern

⚠️ 对比 killall:killall wor 会报 "no process found"(它按精确名匹配,详见《killall 命令详解与参数实测(Ubuntu 22.04)》),而 pkill wor 部分匹配直接杀——pkill 更灵活也更容易误伤,killall 更严格更安全。

3.10 -USR1 自定义信号送达验证

信号不止"杀"一种用途。worker 注册 USR1 处理器(模拟"滚动日志"),pkill -USR1 -f 标记 定向投递,再发默认 TERM 让它退出:

cat > /root/demo/pkill/usr1-worker.sh <<'EOF'
#!/bin/bash
trap 'echo "USR1 received at $(date +%T), rolling log"' USR1
trap 'echo "TERM received, exiting"; exit 0' TERM
echo "usr1-worker pid=$$"
sleep 300 &
while :; do wait; done
EOF
root@ubuntu2204:~# bash /root/demo/pkill/usr1-worker.sh --usr1-marker & UP=$!; sleep 0.5; pkill -USR1 -f usr1-marker; echo usr1_send_rc=$?; sleep 0.5; pkill -f usr1-marker; wait $UP 2>/dev/null; echo worker_rc=$?
usr1-worker pid=384
usr1_send_rc=0
USR1 received at 10:29:27, rolling log
TERM received, exiting
worker_rc=0

时间戳(10:29:27)证明 USR1 确实在 pkill -USR1 执行后送达 worker,最后 TERM 让其优雅退出 rc=0。结论:pkill -USR1 等任意信号均可投递,配合唯一标记可实现"向指定进程群广播控制信号"。

3.11 安全实践:用唯一标记模式替代"pkill bash"式裸杀

为什么不能演示 pkill bash? 因为它按部分模式匹配进程名 bash,会命中当前 Shell、外层脚本、以及系统里所有 bash 进程——实测脚本一执行就会把自己的执行环境杀掉,终端直接断线。这条命令本身就是运维事故的经典来源。

安全替代方案:给目标进程打唯一标记,用 -f 标记 精确打击。把 bash 复制为改名进程 mybash,启动时携带标记参数 --demo-tag-42x:

root@ubuntu2204:~# cp -f /bin/bash /root/demo/pkill/mybash; /root/demo/pkill/mybash -c 'while :; do sleep 300; done' --demo-tag-42x & MB=$!; sleep 0.5; ps -eo pid,comm,args | grep '[m]ybash'
    407 mybash          /root/demo/pkill/mybash -c while :; do sleep 300; done --demo-tag-42x
root@ubuntu2204:~# pkill -f 'demo-tag-42x'; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -f demo-tag-42x || echo tagged-bash-gone
tagged-bash-gone

目标 bash 进程(comm 是 mybash、命令行含唯一标记 42x)被精确清除,当前 Shell 安然无恙——这就是可安全实操的等价演示。

3.12 无匹配 rc=1

与 pgrep 一致:无匹配时静默退出、退出码 1,方便脚本用 &&/|| 消费:

root@ubuntu2204:~# pkill -f zzz-no-such-marker-999; echo rc=$?
rc=1

四、实战示例

4.1 批量清理测试残留

压测/联调结束后清理自己拉起的整组进程。先用锚定模式发 TERM(优雅),再对残存者升级 KILL——与 kill 篇"先 TERM 后 KILL"的兜底顺序完全一致:

root@ubuntu2204:~# pkill -9 -f '^sleep 300$'; echo rc=$?
bash: line 3:   337 Killed                  sleep 300
bash: line 3:   338 Killed                  sleep 300
bash: line 3:   339 Killed                  sleep 300
rc=0
root@ubuntu2204:~# pkill --signal KILL -f '^sleep 120$'; echo rc=$?
bash: line 3:   340 Killed                  sleep 120
rc=0
root@ubuntu2204:~# sleep 0.3; pgrep -c -f '^sleep (300|120)$' || echo all-root-sleep-dead
0
all-root-sleep-dead

💡 要点:先 pgrep -f 模式 预演确认命中面,再原样换成 pkill 执行——两条命令匹配引擎相同,预演结果就是打击清单。

4.2 踢掉指定终端的僵死会话

处理僵死登录会话:定位 pts 编号后整段踢除(3.6 已实测完整链路,此处给场景化闭环):

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 session_pid=$SCD child=$CHILD tty=$TTY
session_pid=392 child=394 tty=pts/0
root@ubuntu2204:~# pkill -t $TTY; echo rc=$?
rc=0
root@ubuntu2204:~# sleep 0.5; pgrep -t $TTY -l || echo pty-session-killed
pty-session-killed

4.3 自定义信号做"运行时控制"

信号不止"杀"一种用途——USR1/USR2 常被长驻进程用作"滚动日志""重开连接"等运行时控制指令。完整闭环(3.10 已实测核心命令):worker 注册 USR1 处理器 → pkill -USR1 -f usr1-marker 定向投递 → worker 打印滚动记录 → pkill -f usr1-marker 发 TERM 优雅收尾:

root@ubuntu2204:~# bash /root/demo/pkill/usr1-worker.sh --usr1-marker & UP=$!; sleep 0.5; pkill -USR1 -f usr1-marker; echo usr1_send_rc=$?; sleep 0.5; pkill -f usr1-marker; wait $UP 2>/dev/null; echo worker_rc=$?
usr1-worker pid=384
usr1_send_rc=0
USR1 received at 10:29:27, rolling log
TERM received, exiting
worker_rc=0

相比 kill 需要先 pgrep 拿 PID,pkill 一条命令完成"找 + 发",对多实例进程群的批量控制优势明显;精确性靠唯一标记模式(-f 唯一标记)保证,这也是 3.11 安全实践的自然延伸。

五、注意事项与常见问题

先给出本组四件工具的选型速查,再进入逐条 Q&A——

💡 技术拓展:kill / pgrep / pkill / killall 选型对照——

维度killpgreppkillkillall
定位按 PID/作业号发信号按模式查 PID(不动手)按模式发信号按精确进程名发信号
匹配方式不匹配,直接给 PIDERE 部分匹配(comm 或 -f 全命令行)同 pgrep精确整名(-r 才用正则)
一次目标数显式列出的若干 PID输出全部命中全部命中全部同名
作业号 %1支持(bash 内置)不支持不支持不支持
默认信号SIGTERM不发信号SIGTERMSIGTERM
典型场景已知 PID 的精确操作预演/探测/组合命令批量清理、模式化运维按名停服务,防误伤要求高时

组合建议:不确定命中面时先 pgrep 预演 → 再 pkill/kill 落地;对"按名杀、宁可杀不中不可杀错"的场合选 killall。

Q1:为什么"pkill bash"是高危命令? A:默认部分匹配下它会命中所有 bash 进程——包括你自己正在用的这个 Shell。虽然 procps 会排除 pkill 自身,但排除不了同名的外层 Shell/SSH 会话,实测脚本里跑它会当场断线。安全做法:给目标进程携带唯一标记参数,用 pkill -f 唯一标记 精确打击(3.9 实测演示)。

Q2:pkill 和 killall 有什么区别? A:pkill 按正则模式匹配(可部分命中、可用 -f 匹配全命令行);killall 按精确进程名匹配(killall wor 直接报无此进程)。pkill 灵活但需防误伤,killall 严格但只认完整名。二者默认信号都是 TERM。

Q3:-f 匹配全命令行有什么风险? A:与 pgrep 相同——包装进程(su tester -c sleep 77、bash run.sh)的命令行里带着目标字样会被一并命中。发信号前先用同模式的 pgrep 预演(两者匹配引擎一致),或加 ^...$ 锚定收窄。

Q4:输出里突然出现的 "Terminated"、"Killed" 是 pkill 打印的吗? A:不是。pkill 本身静默(除非 -e 回显)。这些行来自外层 Shell 对被杀后台作业的终止通知,或 su 等会话管理进程检测到子进程被信号终止时打印的会话消息(3.3/3.4 实测现场)。

Q5:pkill 后 rc=0 但进程还活着? A:rc=0 只表示"信号已投递",不代表进程会退出——TERM 可被捕获或忽略(kill 篇 3.6/3.8 有完整实证)。先稍等再用 pgrep -c 复查,仍活着再升级 pkill -9。

Q5a:pkill -c 能当"预演"用吗? A:不能。实测证明 pkill -c 在报数的同时照常投递信号(3.7 中计数 2 之后进程全部阵亡)。安全预演请用 pgrep -c(pgrep 从不发信号),或 pkill --signal 0 模式 做"零信号"探测式演练。

Q6:如何只杀某个用户的进程? A:pkill -u 用户名 模式(3.3 实测)。pkill -u 用户名 不带模式时会终止该用户名下当前用户有权杀的全部进程,等同于把对方登出,务必慎用。

六、参考链接