Linux pkill命令

pkill命令是pgrep的扩展,可以根据进程名、完整命令行、用户等多种属性匹配并发送信号给进程,比killall更灵活强大。
注意:pkill使用正则表达式匹配,使用时需特别小心,避免误杀重要进程。建议先使用pgrep测试匹配结果。

一、命令简介

pkill命令是procps工具包的一部分,它通过模式匹配来查找进程并发送信号。pkill不仅可以按进程名匹配,还可以按完整命令行、用户、终端等多种属性进行匹配,功能比killall更加强大和灵活。

主要特点:

  • 支持正则表达式模式匹配
  • 可以匹配进程名或完整命令行
  • 支持按用户、终端、组等属性过滤
  • 可以匹配最近启动的进程
  • 支持反转匹配(排除匹配项)
  • 匹配精确控制能力强

二、命令语法

pkill [选项] [信号] 模式
pkill -信号 模式

三、常用选项

选项 说明
-f, --full 匹配完整命令行(而不仅仅是进程名)
-x, --exact 精确匹配进程名
-n, --newest 只匹配最新(最近启动)的进程
-o, --oldest 只匹配最旧(最早启动)的进程
-u, --euid 按有效用户ID匹配
-U, --uid 按实际用户ID匹配
-G, --gid 按组ID匹配
-t, --terminal 按终端匹配(tty或pts)
-P, --parent 按父进程ID匹配
-s, --session 按会话ID匹配
-v, --inverse 反转匹配(排除匹配的进程)
-i, --ignore-case 忽略大小写匹配
-c, --count 只显示匹配的进程数,不发送信号
-d, --delimiter 指定输出分隔符(默认换行)
-l, --list-name 列出信号名称
-a, --list-full 列出匹配进程的完整命令行
-q, --quiet 安静模式,不显示错误信息
-signal 指定要发送的信号(默认SIGTERM)

四、常用信号(与killall相同)

信号 编号 说明
SIGHUP 1 挂起信号,重新读取配置文件
SIGINT 2 中断信号(Ctrl+C)
SIGQUIT 3 退出信号
SIGKILL 9 强制终止信号(不可捕获)
SIGTERM 15 终止信号(默认),允许进程清理
SIGSTOP 19 暂停信号(不可捕获)
SIGCONT 18 继续执行信号

五、使用示例

1. 基本用法

# 终止所有firefox进程(默认发送SIGTERM)
pkill firefox

# 强制终止所有firefox进程
pkill -9 firefox
pkill -KILL firefox

# 精确匹配进程名
pkill -x bash

2. 匹配完整命令行(-f选项)

# 匹配进程名中包含"python"的进程
pkill python

# 匹配完整命令行中包含"python script.py"的进程
pkill -f "python script.py"

# 匹配启动特定应用的进程
pkill -f "java.*myapp.jar"
pkill -f "node.*server.js"

# 匹配带参数的进程
pkill -f "nginx.*-c /etc/nginx/nginx.conf"

3. 按用户过滤

# 终止指定用户的所有进程
pkill -u username
pkill -U 1000  # 按UID终止

# 终止除root用户外的所有bash进程
pkill -u root bash

# 终止www-data用户的所有nginx进程
pkill -u www-data nginx

# 终止多个用户的进程
pkill -u user1,user2,user3

4. 按终端过滤

# 终止指定终端的所有进程
pkill -t tty1
pkill -t pts/0

# 终止pts/0终端的所有bash进程
pkill -t pts/0 bash

# 终止所有终端(除当前终端外)的进程
pkill -t pts/  # 注意:这很危险!

5. 按时间过滤(-n, -o选项)

# 终止最近启动的firefox进程
pkill -n firefox

# 终止最早启动的firefox进程
pkill -o firefox

# 终止最近启动的5个python进程
for i in {1..5}; do pkill -n python; done

# 只终止最新启动的SSH连接
pkill -n -f "ssh.*user@host"

6. 反转匹配和忽略大小写

# 终止除bash外的所有进程(当前用户)
pkill -v bash

# 终止除root用户外的所有进程
pkill -v -u root

# 忽略大小写匹配
pkill -i CHROME
pkill -i -f "python.*SCRIPT"

7. 高级正则表达式匹配

# 匹配以"worker"开头的进程
pkill "^worker"

# 匹配包含"test"的进程
pkill ".*test.*"

# 匹配数字结尾的进程
pkill "worker_[0-9]+"

# 匹配特定模式的进程
pkill -f "(python|perl).*\.py"

# 匹配特定端口的进程
pkill -f ":8080"
pkill -f "nginx.*80"

8. 实际应用场景

# 重启Web服务器(优雅重启)
pkill -HUP nginx
pkill -HUP apache2

# 终止用户的所有SSH会话
pkill -u username ssh
pkill -u username sshd

# 清理僵尸进程的父进程
pkill -9 -P $(ps aux | awk '$8=="Z" {print $3}')

# 终止所有Chrome标签页进程
pkill -f chrome.*--type=renderer

# 终止特定的Docker容器进程
pkill -f "docker.*container_name"

# 终止屏幕保护程序
pkill xscreensaver
pkill -f "gnome-screensaver"

9. 测试和计数模式

# 只显示匹配的进程数,不实际终止
pgrep firefox | wc -l
pkill -c firefox  # 更简单的方式

# 列出匹配的进程详细信息
pgrep -l firefox
pkill -l firefox  # 显示进程名和PID

# 显示完整命令行
pgrep -a firefox
pkill -a firefox  # pkill的等价方式

# 测试匹配结果(使用pgrep)
pgrep -f "python.*script"
pgrep -u www-data

六、pkill vs pgrep vs killall

命令 主要功能 特点 示例
pkill 模式匹配并发送信号 支持正则表达式,功能最强大 pkill -f "python.*script"
pgrep 模式匹配查找PID 只查找不终止,用于测试 pgrep -l firefox
killall 按进程名终止 简单直观,不支持正则表达式 killall firefox
kill 按PID终止 需要知道PID,最精确 kill 1234

七、实用技巧

技巧1:安全使用pkill的步骤
#!/bin/bash
# safe_pkill.sh - 安全使用pkill的步骤

PATTERN="$1"
SIGNAL="${2:-TERM}"  # 默认信号为TERM

echo "=== 安全终止进程: 模式='$PATTERN', 信号=$SIGNAL ==="

# 1. 先用pgrep测试匹配结果
echo "1. 测试匹配结果:"
MATCH_COUNT=$(pgrep -c -f "$PATTERN")
if [ "$MATCH_COUNT" -eq 0 ]; then
    echo "  未找到匹配的进程"
    exit 0
fi

echo "  找到 $MATCH_COUNT 个匹配进程"

# 2. 显示匹配进程的详细信息
echo "2. 匹配进程详情:"
pgrep -a -f "$PATTERN"

# 3. 交互确认
read -p "3. 确认终止这些进程? (y/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    echo "操作已取消"
    exit 0
fi

# 4. 先尝试优雅终止
echo "4. 发送 SIG$SIGNAL 信号..."
pkill -$SIGNAL -f "$PATTERN"

# 5. 等待并检查
sleep 2
REMAINING=$(pgrep -c -f "$PATTERN")
if [ "$REMAINING" -gt 0 ]; then
    echo "  仍有 $REMAINING 个进程存活"

    if [ "$SIGNAL" != "KILL" ]; then
        read -p "  尝试强制终止? (y/n): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            echo "  发送 SIGKILL..."
            pkill -9 -f "$PATTERN"
        fi
    fi
else
    echo "  所有进程已终止"
fi
技巧2:批量管理用户会话
#!/bin/bash
# user_session_manager.sh - 批量管理用户会话

ACTION="$1"
USERNAME="$2"

case "$ACTION" in
    list)
        echo "当前活跃用户会话:"
        who | awk '{print $1, $2, $5}' | sort | uniq

        echo -e "\n按用户统计:"
        who | awk '{print $1}' | sort | uniq -c

        echo -e "\n按终端统计:"
        who | awk '{print $2}' | sort | uniq -c
        ;;

    kill-user)
        if [ -z "$USERNAME" ]; then
            echo "请指定用户名"
            exit 1
        fi

        echo "终止用户 $USERNAME 的所有会话:"

        # 先列出会话
        echo "会话列表:"
        who | grep "^$USERNAME"

        # 询问确认
        read -p "确认终止? (y/n): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            exit 0
        fi

        # 终止用户的所有进程
        pkill -u "$USERNAME"

        # 强制终止剩余进程
        sleep 2
        pkill -9 -u "$USERNAME" 2>/dev/null

        echo "操作完成"
        ;;

    kill-terminal)
        TERMINAL="$2"
        if [ -z "$TERMINAL" ]; then
            echo "请指定终端 (如 pts/0, tty1)"
            exit 1
        fi

        echo "终止终端 $TERMINAL 的所有进程:"
        pkill -t "$TERMINAL"
        ;;

    *)
        echo "用法:"
        echo "  $0 list                       # 列出所有会话"
        echo "  $0 kill-user username         # 终止指定用户的所有会话"
        echo "  $0 kill-terminal terminal     # 终止指定终端的所有进程"
        exit 1
        ;;
esac
技巧3:监控和清理僵尸进程
#!/bin/bash
# zombie_cleaner.sh - 监控和清理僵尸进程

MONITOR_INTERVAL=60  # 监控间隔(秒)
LOG_FILE="/var/log/zombie_cleaner.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

clean_zombies() {
    # 查找僵尸进程
    ZOMBIE_PIDS=$(ps aux | awk '$8=="Z" {print $2}')

    if [ -z "$ZOMBIE_PIDS" ]; then
        log "未发现僵尸进程"
        return 0
    fi

    ZOMBIE_COUNT=$(echo "$ZOMBIE_PIDS" | wc -l)
    log "发现 $ZOMBIE_COUNT 个僵尸进程: $ZOMBIE_PIDS"

    # 获取僵尸进程的父进程
    for pid in $ZOMBIE_PIDS; do
        PARENT_PID=$(ps -o ppid= -p "$pid" | tr -d ' ')
        PARENT_CMD=$(ps -o cmd= -p "$PARENT_PID")

        log "僵尸进程 $pid (父进程: $PARENT_PID, 命令: $PARENT_CMD)"

        # 尝试向父进程发送SIGCHLD信号
        kill -CHLD "$PARENT_PID" 2>/dev/null

        # 如果父进程是init(1)或者系统进程,不要终止
        if [ "$PARENT_PID" -ne 1 ] && [[ ! "$PARENT_CMD" =~ systemd|init ]]; then
            # 等待一段时间后检查
            sleep 1
            if ps -p "$pid" > /dev/null 2>&1; then
                log "  尝试终止父进程 $PARENT_PID"
                kill -TERM "$PARENT_PID" 2>/dev/null
                sleep 1
                if ps -p "$PARENT_PID" > /dev/null 2>&1; then
                    log "  强制终止父进程 $PARENT_PID"
                    kill -KILL "$PARENT_PID" 2>/dev/null
                fi
            fi
        fi
    done

    # 再次检查
    sleep 2
    REMAINING_ZOMBIES=$(ps aux | awk '$8=="Z"' | wc -l)
    if [ "$REMAINING_ZOMBIES" -gt 0 ]; then
        log "仍有 $REMAINING_ZOMBIES 个僵尸进程"
    else
        log "僵尸进程已清理"
    fi
}

# 主循环
log "僵尸进程清理器启动"
while true; do
    clean_zombies
    sleep "$MONITOR_INTERVAL"
done
技巧4:进程资源监控和限制
#!/bin/bash
# process_monitor.sh - 进程资源监控和限制

THRESHOLD_CPU=80   # CPU使用率阈值(%)
THRESHOLD_MEM=70   # 内存使用率阈值(%)
CHECK_INTERVAL=30  # 检查间隔(秒)
LOG_FILE="/var/log/process_monitor.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

monitor_processes() {
    log "开始监控进程资源使用情况..."

    # 获取高CPU使用率的进程
    HIGH_CPU_PIDS=$(ps -eo pid,%cpu,cmd --sort=-%cpu | awk -v threshold="$THRESHOLD_CPU" '$2 > threshold && NR>1 {print $1, $2, $3}' | head -10)

    if [ -n "$HIGH_CPU_PIDS" ]; then
        log "高CPU使用率进程:"
        echo "$HIGH_CPU_PIDS" | while read pid cpu cmd; do
            log "  PID:$pid CPU:${cpu}% CMD:$cmd"

            # 检查是否是关键进程
            if [[ "$cmd" =~ systemd|init|sshd|dbus ]]; then
                log "    关键进程,跳过"
            else
                # 降低进程优先级
                renice +10 "$pid" 2>/dev/null
                log "    已降低优先级"

                # 如果CPU使用率超过90%,考虑终止
                if (( $(echo "$cpu > 90" | bc -l) )); then
                    read -p "进程 $pid ($cmd) CPU使用率 ${cpu}%,是否终止? (y/n): " -n 1 -r
                    echo
                    if [[ $REPLY =~ ^[Yy]$ ]]; then
                        kill -TERM "$pid"
                        log "    已发送终止信号"
                    fi
                fi
            fi
        done
    fi

    # 获取高内存使用率的进程
    HIGH_MEM_PIDS=$(ps -eo pid,%mem,cmd --sort=-%mem | awk -v threshold="$THRESHOLD_MEM" '$2 > threshold && NR>1 {print $1, $2, $3}' | head -10)

    if [ -n "$HIGH_MEM_PIDS" ]; then
        log "高内存使用率进程:"
        echo "$HIGH_MEM_PIDS" | while read pid mem cmd; do
            log "  PID:$pid MEM:${mem}% CMD:$cmd"

            # 如果是Java进程,尝试触发GC
            if [[ "$cmd" =~ java ]]; then
                log "    Java进程,尝试发送GC信号"
                kill -3 "$pid" 2>/dev/null
            fi
        done
    fi

    # 检查僵尸进程
    ZOMBIE_COUNT=$(ps aux | awk '$8=="Z"' | wc -l)
    if [ "$ZOMBIE_COUNT" -gt 0 ]; then
        log "发现 $ZOMBIE_COUNT 个僵尸进程"
    fi
}

# 主循环
while true; do
    monitor_processes
    sleep "$CHECK_INTERVAL"
done

八、注意事项

  • 正则表达式风险:pkill默认使用正则表达式,.会匹配任意字符,容易误匹配
  • 完整命令行匹配:使用-f选项时要特别小心,可能匹配到不预期的进程
  • 测试优先:始终先用pgrep测试匹配结果,确认无误后再使用pkill
  • 权限限制:普通用户只能终止自己的进程,root用户可以终止所有进程
  • 系统进程:不要随意终止系统关键进程(如systemd、init、dbus等)
  • 信号顺序:优先使用SIGTERM(15),避免直接使用SIGKILL(9)
  • 会话管理:终止用户所有进程可能导致用户会话中断,需谨慎操作
  • 生产环境:生产环境中建议使用systemd等专业工具管理进程

九、常见问题

killall命令:

  • 只按进程名匹配
  • 不支持正则表达式(除非使用-r选项)
  • 匹配相对简单直接
  • 语法:killall 进程名

pkill命令:

  • 支持正则表达式模式匹配
  • 可以匹配进程名或完整命令行(-f选项)
  • 支持按用户、终端、组等属性过滤
  • 功能更强大灵活
  • 语法:pkill 模式

示例对比:

# killall只能按进程名
killall firefox

# pkill可以使用正则表达式
pkill "^firefox.*"
pkill -f "firefox.*--profile"

# killall需要-r选项才能使用正则表达式
killall -r "^firefox.*"

避免误杀的策略:

# 1. 始终先用pgrep测试
pgrep -l firefox
pgrep -a -f "python.*script"

# 2. 使用更精确的模式
# 错误:可能匹配到不相关的进程
pkill "python"

# 正确:更精确的匹配
pkill -f "python.*/home/user/script.py"
pkill -x "python3.9"  # 精确匹配

# 3. 使用-f选项时要小心
# 危险:可能匹配到很多进程
pkill -f "java"

# 更安全:限定更具体的模式
pkill -f "java.*myapp.jar"
pkill -f "java -jar /opt/app/server.jar"

# 4. 结合用户过滤
pkill -u username firefox

# 5. 使用交互模式(脚本中)
read -p "确认终止? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    pkill firefox
fi

# 6. 先尝试发送TERM信号
pkill firefox
sleep 2
# 检查是否还有进程
if pgrep firefox > /dev/null; then
    echo "仍有firefox进程,需要强制终止吗?"
    read -p "(y/n): " -n 1 -r
    echo
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        pkill -9 firefox
    fi
fi

pgreppkill是同一工具包(procps)中的两个命令,它们使用相同的匹配逻辑:

pgrep命令:

  • 查找并显示匹配的进程PID
  • 只查找,不发送信号
  • 用于测试匹配结果

pkill命令:

  • 查找并发送信号给匹配的进程
  • 实际执行终止操作
  • 使用前应该先用pgrep测试

两者关系示例:

# 1. 先用pgrep测试匹配
pgrep -l firefox
pgrep -a -f "python.*script"

# 2. 查看匹配数量
pgrep -c firefox

# 3. pkill使用相同的选项
pkill firefox
pkill -f "python.*script"

# 4. pgrep的大部分选项在pkill中也可用
pgrep -u username
pkill -u username

pgrep -t pts/0
pkill -t pts/0

# 5. 使用pgrep作为pkill的安全前置检查
PATTERN="firefox"
if pgrep -c "$PATTERN" -gt 0; then
    echo "找到 $(pgrep -c "$PATTERN") 个匹配进程"
    pgrep -l "$PATTERN"
    # 确认后再使用pkill
fi

有几种方法可以终止特定端口的进程:

# 方法1: 使用lsof找到PID再kill
PORT=8080
PID=$(lsof -ti:$PORT)
if [ -n "$PID" ]; then
    kill $PID
fi

# 方法2: 使用一行命令
lsof -ti:8080 | xargs kill

# 方法3: 使用fuser命令
fuser -k 8080/tcp

# 方法4: 使用pkill结合netstat或ss
# 查找监听8080端口的进程
netstat -tlnp | grep :8080 | awk '{print $7}' | cut -d'/' -f1 | xargs kill

# 方法5: 使用pkill -f匹配端口号
# 注意:这种方法可能不准确
pkill -f ":8080"
pkill -f ".*:8080.*"

# 方法6: 使用ss命令
ss -tlnp | grep :8080 | awk '{print $NF}' | cut -d',' -f2 | cut -d'=' -f2 | xargs kill

# 推荐的脚本
kill_port() {
    local port="$1"
    local signal="${2:-TERM}"

    echo "查找端口 $port 的进程..."

    # 使用lsof
    PIDS=$(lsof -ti:"$port")

    if [ -z "$PIDS" ]; then
        # 尝试使用ss
        PIDS=$(ss -tlnp | awk -v port=":$port" '$4 ~ port {split($6, a, "="); print a[2]}' | sort -u)
    fi

    if [ -z "$PIDS" ]; then
        echo "未找到使用端口 $port 的进程"
        return 1
    fi

    echo "找到进程: $PIDS"

    # 显示进程信息
    for pid in $PIDS; do
        ps -p "$pid" -o pid,user,cmd
    done

    read -p "确认终止这些进程? (y/n): " -n 1 -r
    echo
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        for pid in $PIDS; do
            kill -$signal "$pid"
            echo "已发送 SIG$signal 给进程 $pid"
        done
    else
        echo "操作取消"
    fi
}

# 使用示例
kill_port 8080
kill_port 3000 KILL

十、最佳实践

pkill使用最佳实践
  1. 先测试后执行:始终先用pgrep测试匹配结果
  2. 使用精确模式:避免使用过于宽泛的正则表达式
  3. 结合属性过滤:使用-u-t等选项缩小匹配范围
  4. 优先使用SIGTERM:给进程清理的机会,避免数据丢失
  5. 记录操作日志:重要的终止操作前记录日志
  6. 编写安全脚本:复杂的操作封装到脚本中,加入确认机制
  7. 了解进程关系:终止父进程前了解子进程影响
  8. 备份重要数据:终止数据库等关键进程前备份数据
  9. 生产环境谨慎:生产环境避免直接使用pkill,使用systemd等专业工具
  10. 学习信号机制:理解不同信号的作用和影响