随机
Enter 搜索 ↑↓ 切换 Esc 清空

kill-ffmpeg-tree

脚本

一键终止全部 ffmpeg 及其子进程树

补充说明

该脚本用于彻底清理系统上全部 ffmpeg 进程:不仅杀掉 ffmpeg 本身,还会连同它的整棵子进程树(通过 /proc/PID/task/PID/children 递归收集所有子孙进程),以及启动它的父进程及父进程的整棵子孙树(避免部分 ffmpeg 由脚本/守护进程拉起、只杀叶子会反复重建的情况)。

先对所有目标进程发送 SIGTERM(让 ffmpeg 优雅退出、尽量冲洗输出文件),等 2 秒后复查,仍未退出的再 SIGKILL 兜底强杀。全程静默执行,无任何输出;没有一个 ffmpeg 进程时直接 exit 0。

它正是 ffmpeg_video_h264_all / ffmpeg_video_hvc1_all 批量转码脚本启动提示里 pkill -9 -f ffmpeg 的进阶替代:同样能停掉所有转码任务,还多一层「等 2 秒先礼貌终止、再强杀残留」与「连父进程子树一起清理」。

⚠ 用途慎重:机器上任何正在运行的 ffmpeg 都会被终止(包括媒体服务缩略图/封面提取、直播推拉流、录屏等任务),而且进程的父进程也会被杀。仅在确认当前无其他重要 ffmpeg 任务时使用。

一键脚本

bash <(curl -sL gitee.com/meimolihan/cmdbox/raw/master/sh/kill-ffmpeg-tree.sh)
bash <(curl -sL ../sh/kill-ffmpeg-tree.sh)

使用示例(脚本名以实际部署路径为准,以下为本地脚本名):

chmod +x kill-ffmpeg-tree.sh

# 一键终止全部 ffmpeg 及其进程树(静默执行)
./kill-ffmpeg-tree.sh

# 转码/媒体服务场景:先用它停掉所有 ffmpeg,再继续后续清理
./kill-ffmpeg-tree.sh && rm -f /tmp/videnc_batch.lock

脚本不需要任何参数,也没有交互确认;执行完自动返回。想看过程可临时 bash -x 跟踪。

运行环境

功能特点

执行流程

1. FFMPEG_PIDS=$(pidof ffmpeg)      # 找出所有名为 ffmpeg 的进程
2. 若无则 exit 0                    # 没有任务,直接返回
3. 对每个 ffmpeg:
   - 加入该 PID
   - 读 ppid(ps),若非 1(不是 init/容器主进程)则:
     - 加入父进程 PID
     - collect_tree 父进程 → 递归收集父进程全部子孙
   - collect_tree ffmpeg → 递归收集其全部子孙(/proc children)
4. UNIQ=去重并按数值排序
5. kill 全部(SIGTERM)            # 优雅退出机会
6. sleep 2
7. 复查仍存活的 PID → SIGKILL 兜底  # 强制清理

子进程收集核心:

collect_tree() {
    local pids=("$@")
    local all=("${pids[@]}")
    for pid in "${pids[@]}"; do
        if [[ -r "/proc/$pid/task/$pid/children" ]]; then
            read -ra children < "/proc/$pid/task/$pid/children"
            all+=( "${children[@]}" $(collect_tree "${children[@]}") )   # 递归
        fi
    done
    echo "${all[@]}"
}

注意事项

脚本源码

#!/bin/bash
set -uo pipefail
gl_hui='\033[38;5;59m'
gl_hong='\033[38;5;9m'
gl_lv='\033[38;5;10m'
gl_huang='\033[38;5;11m'
gl_lan='\033[38;5;32m'
gl_bai='\033[38;5;15m'
gl_zi='\033[38;5;13m'
gl_bufan='\033[38;5;14m'

log_info() { echo -e "${gl_lan}[信息]${gl_bai} $*"; }
log_ok() { echo -e "${gl_lv}[成功]${gl_bai} $*"; }
log_warn() { echo -e "${gl_huang}[警告]${gl_bai} $*"; }
log_error() { echo -e "${gl_hong}[错误]${gl_bai} $*" >&2; }

sleep_fractional() {
    local seconds=$1
    if sleep "$seconds" 2>/dev/null; then return 0; fi
    if command -v perl >/dev/null 2>&1; then perl -e "select(undef, undef, undef, $seconds)"; return 0; fi
    if command -v python3 >/dev/null 2>&1; then python3 -c "import time; time.sleep($seconds)"; return 0; fi
    if command -v python >/dev/null 2>&1; then python -c "import time; time.sleep($seconds)"; return 0; fi
    local int_seconds=$(echo "$seconds" | awk '{print int($1+0.999)}')
    sleep "$int_seconds"
}

collect_tree() {
    local pids=("$@")
    local all=("${pids[@]}")
    local children
    for pid in "${pids[@]}"; do
        if [[ -r "/proc/$pid/task/$pid/children" ]]; then
            read -ra children < "/proc/$pid/task/$pid/children"
            if [[ ${#children[@]} -gt 0 ]]; then
                all+=("${children[@]}")
                all+=($(collect_tree "${children[@]}"))
            fi
        fi
    done
    echo "${all[@]}"
}

kill_ffmpeg_tree() {
    log_info "开始查找所有 ffmpeg 进程"
    FFMPEG_PIDS=($(pidof ffmpeg || true))
    if [[ ${#FFMPEG_PIDS[@]} -eq 0 ]]; then
        log_ok "未检测到任何 ffmpeg 进程,无需处理"
        return 0
    fi
    log_info "检测到 ffmpeg PID列表: ${FFMPEG_PIDS[*]}"

    TREE_PIDS=()
    for fp in "${FFMPEG_PIDS[@]}"; do
        TREE_PIDS+=("$fp")
        log_info "正在获取 ffmpeg[$fp] 的父进程信息"
        pp=$(ps -o ppid= -p "$fp" | tr -d ' ')
        if [[ "$pp" != "1" && -n "$pp" ]]; then
            TREE_PIDS+=("$pp")
            log_info "ffmpeg[$fp] 父进程 PID: $pp,递归收集完整进程树"
            TREE_PIDS+=($(collect_tree "$pp"))
        fi
    done

    UNIQ_PIDS=($(printf "%s\n" "${TREE_PIDS[@]}" | sort -nu))
    log_info "汇总待清理进程PID列表: ${UNIQ_PIDS[*]}"

    log_info "第一步:发送 SIGTERM 优雅终止进程"
    kill "${UNIQ_PIDS[@]}" 2>/dev/null
    log_info "等待2秒,等待进程正常退出"
    sleep_fractional 2

    REMAIN=()
    for p in "${UNIQ_PIDS[@]}"; do
        if ps -p "$p" >/dev/null 2>&1; then
            REMAIN+=("$p")
        fi
    done

    if [[ ${#REMAIN[@]} -gt 0 ]]; then
        log_warn "存在未能正常退出的残留进程: ${REMAIN[*]},执行强制 kill -9"
        kill -9 "${REMAIN[@]}" 2>/dev/null
        log_ok "残留进程强制终止完成"
    else
        log_ok "所有进程已收到SIGTERM并正常退出,无残留"
    fi

    log_info "二次校验 ffmpeg 进程状态"
    CHECK_FFMPEG=($(pidof ffmpeg || true))
    if [[ ${#CHECK_FFMPEG[@]} -eq 0 ]]; then
        log_ok "✅ 校验通过:ffmpeg 进程已全部清除"
        return 0
    else
        log_error "❌ 校验失败,仍存在ffmpeg进程:${CHECK_FFMPEG[*]}"
        return 1
    fi
}

# 免交互直接执行入口
kill_ffmpeg_tree
exit $?

创建本地脚本

new_script="kill-ffmpeg-tree.sh"

cat > "$new_script" << 'EOF'
#!/bin/bash

# 粘贴上方脚本源码

EOF

# 保留本地脚本,去掉 rm -f "$new_script"
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"

相关命令