补充说明
该脚本用于彻底清理系统上全部 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 跟踪。
运行环境
- 系统:Linux(依赖
/proc接口与pidof/ps) - 权限:能对目标进程发信号即可;若要终止其他用户(如 root 起的媒体服务)的 ffmpeg,需要 root/sudo 执行
- 依赖:
pidof(procps 工具包)、ps、sort,均为标准组件
功能特点
- 一键终止系统上全部 ffmpeg 进程,无需手动逐个查找 PID
- 递归收集子进程树(
/proc/PID/task/PID/children),连孙进程、重孙进程一并清理 - 顺带把启动 ffmpeg 的父进程(非 init)及其整棵子孙树纳入清理,杜绝「杀了又重建」
- SIGTERM 优雅终止 → 等 2 秒 → 复查残留 → SIGKILL 兜底强杀
- 全程静默,无任何交互与输出;没有 ffmpeg 进程时返回 0
- PID 去重排序(
sort -nu),同一进程不会被重复 kill - 对瞬时退出、已被其他进程回收的 PID 做了容错(忽略无效信号目标)
执行流程
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[@]}"
}
注意事项
- 无差别清场:机器上所有 ffmpeg(含媒体服务的封面/缩略图提取、录屏、直播推拉流)都会被终止,执行前务必确认
- 会连父进程一起杀:若某个 ffmpeg 由长驻守护进程当作子进程拉起(如 NAS 媒体索引/转码服务),该守护进程及其整棵子树也会被终止,可能导致相关服务瘫痪
- SIGKILL(
kill -9)兜底只对 2 秒后仍存活的进程使用:ffmpeg 正常响应 SIGTERM 时会优雅退出;个别僵死进程才会被强杀 - 信号发出后建议观察目标服务的状态;若无自愈机制需手动重启被误杀的服务
- Linux 专属(依赖
/proc);macOS 无/proc/PID/task/PID/children,功能受限 - 其他用户拥有的进程,需 root 权限才能终止,否则对应 PID 静默跳过
- 该脚本无确认环节,属于「一键必杀」工具,建议像转码脚本提示的停止命令一样谨慎调用
脚本源码
#!/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"
相关命令
- kill-ffmpeg-tree 👈 当前所在位置
- ffmpeg_video_h264_all 停止该后台转码任务用
- ffmpeg_video_hvc1_all 停止该后台转码任务用
- ffmpeg_video_hvc1 单目录/文件级 QSV 转码