如何实现前台任务日志输出到stdout与logger,后台任务仅输出到logger且脱离脚本?
问题与解决方案
需求
- 脚本执行
echo及快速返回命令时,其stdout和stderr需同时显示在终端并记录到syslog(使用logger工具) - 在脚本中启动完全脱离的后台守护进程
- 后台任务的输出仅记录到syslog
- 脚本退出后,后台任务可继续运行,不会随调用shell终止
- 脚本进程需立即退出,不被启动的任务占用
尝试的问题
第一种脚本
#!/bin/bash exec > >(tee >(logger -t script) 2>&1 echo "Start" # bash just as an example for the background task bash -c "echo 1; sleep 2; echo 2; sleep 2; echo end" > >(logger -t daemon) 2>&1 < /dev/null & sleep 1 echo "End"
问题:后台子进程继承了tee的FIFO,导致脚本进程需等待后台任务结束才会退出。
第二种脚本(关闭FD 1和2后启动任务)
#!/bin/bash exec > >(tee >(logger -t script) 2>&1 echo "Start" # bash just as an example for the background task exec 1>&- 2>&- bash -c "echo 1; sleep 2; echo 2; sleep 2; echo end" > >(logger -t daemon) 2>&1 < /dev/null & sleep 1 echo "End"
问题:脚本永久挂起,因为logger -t daemon进程始终不结束,脚本进程在子进程退出后仍保持开启。
第三种脚本(使用setsid/nohup)
#!/bin/bash exec > >(tee >(logger -t script) 2>&1 echo "Start" # bash just as an example for the background task exec 1>&- 2>&- setsid bash -c "echo 1; sleep 2; echo 2; sleep 2; echo end" > >(logger -t daemon) 2>&1 < /dev/null & sleep 1 echo "End"
问题:无改善,脚本仍无法立即退出。
解决方案
核心要解决文件描述符继承和进程组脱离两个问题,以下是可行实现:
方案一(使用setsid)
#!/bin/bash # 配置脚本自身输出:同时输出到终端和syslog exec > >(tee >(logger -t script) 2>&1) 2>&1 echo "Start" # 启动完全脱离的后台守护进程 (setsid bash -c 'echo 1; sleep 2; echo 2; sleep 2; echo end' 2>&1 | logger -t daemon >/dev/null 2>&1 &) sleep 1 echo "End"
方案二(使用nohup)
#!/bin/bash # 配置脚本自身输出:同时输出到终端和syslog exec > >(tee >(logger -t script) 2>&1) 2>&1 echo "Start" # 启动完全脱离的后台守护进程 (nohup bash -c 'echo 1; sleep 2; echo 2; sleep 2; echo end' 2>&1 | logger -t daemon >/dev/null 2>&1 &) sleep 1 echo "End"
核心原理说明
- 脚本输出处理:
exec > >(tee >(logger -t script) 2>&1) 2>&1正确将脚本的stdout/stderr同时转发到终端和syslog,注意原脚本缺失闭合的),这也是之前出现异常的细节问题。 - 后台任务脱离:
setsid/nohup会创建新会话/进程组,让后台任务脱离原脚本的进程组,避免脚本退出时收到SIGHUP信号被终止。- 用子shell
(...)包裹启动逻辑,避免当前shell的文件描述符被后台任务继承,解决原脚本中tee的FIFO被持有导致脚本无法退出的问题。 logger -t daemon >/dev/null 2>&1确保logger的输出被丢弃,且后台任务结束后,logger会正确读取完输入并退出,不会导致进程挂起。
- 立即退出保障:后台任务处于独立会话,未继承脚本关键FD,脚本执行到
echo "End"后会立即退出,无需等待后台任务完成。
关键知识点补充
- 文件描述符继承:子进程默认继承父进程所有打开的FD,若父进程有未关闭的管道(如
tee的FIFO),子进程持有该FD会导致父进程无法退出,直到所有持有进程关闭它。 - 进程组与会话:shell退出时会向所属进程组发送SIGHUP信号,
setsid/nohup可让后台任务脱离原进程组,避免收到该信号。 - 管道与进程等待:后台任务输出通过管道连接
logger时,需确保管道两端进程正确结束,避免因管道未关闭导致进程挂起。
内容的提问来源于stack exchange,提问作者maikelmeyers
相关产品推荐
相关产品推荐

