含自定义函数的Bash脚本无法通过systemd服务正常运行的问题排查
我来帮你一步步拆解和解决这个问题,从你描述的三种情况来看,核心是脚本语法错误、阻塞式函数的执行顺序和systemd对进程的管理逻辑这几个点在捣鬼。
一、先揪出最明显的问题:脚本语法错误
你脚本最后一行的代码是:
run_ffmpeg & run_rknn_yolov5_demo & &
这里多了一个多余的&,属于语法错误!Bash遇到这种错误会直接终止脚本执行,这就是Case1里htop看不到任何ffmpeg或rknn进程的根本原因——脚本连启动都没成功。
二、分析不同运行方式的问题根源
Case2:去掉所有&,只执行run_ffmpeg run_rknn_yolov5_demo
run_ffmpeg函数里是v380 | ffmpeg ...的组合,这是一个永远不会主动退出的阻塞式命令(它会一直从摄像头拉流转存图片)。当你去掉&让它前台运行时,脚本会彻底卡在这个函数里,后面的run_rknn_yolov5_demo根本没机会执行,所以你只能看到ffmpeg进程,rknn完全没启动。
Case3:run_ffmpeg & run_rknn_yolov5_demo(唯一能运行的情况)
这个写法是有效的:run_ffmpeg后台运行(脱离脚本的主执行流),run_rknn_yolov5_demo前台运行。此时systemd的Type=simple会把run_rknn_yolov5_demo作为服务的主进程监控,只要这个前台的while循环不退出,服务就会稳定运行;后台的ffmpeg会作为主进程的子进程继续存活。
而如果把两个函数都后台运行(run_ffmpeg & run_rknn_yolov5_demo &),脚本执行完这两行就会立刻退出,systemd会认为服务已经结束,再加上你设置了Restart=always,会反复重启服务,导致进程混乱。
三、给你一套完整的修复和优化方案
1. 修复脚本语法+调整进程运行逻辑
先把脚本最后一行改成下面的写法,确保systemd能监控到稳定的主进程:
# 把ffmpeg后台启动,让rknn处理逻辑作为脚本的前台主进程 run_ffmpeg & run_rknn_yolov5_demo
同时给run_ffmpeg函数加个重启机制,避免摄像头断流后ffmpeg退出就再也不启动:
run_ffmpeg() { while true; do v380 -u xxxx -p xxxx -addr 192.168.1.xxx | ffmpeg -i - -f image2 -vf fps=3 -strftime 1 "$TEMP_DIR/%Y-%m-%d_%H-%M-%S_cap.jpg" -y # 如果ffmpeg意外退出,等2秒后自动重启 echo "ffmpeg进程意外终止,2秒后重启..." sleep 2 done }
2. 解决权限问题(容易忽略的坑)
systemd默认会以root用户运行服务,但你的脚本里用的是$HOME/xxx的路径,还有/media/32GB的存储目录,可能会有权限冲突。给你的systemd服务添加用户指定:
编辑.service文件的[Service]段,加上:
User=xxx Group=xxx
这样服务会以你自己的用户身份运行,避免读写图片目录时出现权限拒绝的错误。
3. 优化systemd服务的稳定性
可以给服务加个进程存活检查的逻辑,或者调整重启策略,比如在[Service]段添加:
# 只有当进程异常退出时才重启,正常退出不重启 Restart=on-failure # 日志输出到journald,方便排查 StandardOutput=journal+console StandardError=journal+console
另外,你原来的ExecStartPre=/bin/sleep 30是为了等网络,可以换成更智能的网络检查(需要先安装wait-for-it工具):
ExecStartPre=/usr/bin/wait-for-it 192.168.1.xxx:80 -t 30
这样会等摄像头的IP地址可以访问后再启动脚本,比固定sleep更靠谱。
四、验证修复效果
修改完脚本和服务文件后,执行以下命令生效:
sudo systemctl daemon-reload sudo systemctl restart process_images_rknn.service # 查看服务状态 sudo systemctl status process_images_rknn.service # 实时查看服务日志,排查可能的错误 journalctl -u process_images_rknn.service -f
如果日志里没有报错,htop里就能看到ffmpeg和rknn_yolov5_demo两个进程正常运行了。
内容来源于stack exchange

