管道中使用ts命令导致shell备份脚本执行后无法退出问题求助
问题产生原因
- 这是Linux管道生命周期的典型特性:管道右侧的
ts、tee进程会持续等待标准输入的EOF信号才会退出,只有当所有持有该管道写端的进程都退出、或者主动关闭写端时,读端才会收到EOF。 - 你调用的
copyJob.sh脚本(或其内部执行的LUKS挂载、rsync等操作)存在后台运行的子进程,这些子进程没有重定向标准输出/错误,继承了管道的写端。即使copyJob.sh主进程执行完成退出,残留的后台子进程仍然持有管道写端,导致ts一直收不到EOF,持续处于阻塞状态,进而导致整个脚本无法退出。 - 你添加的
set -o pipefail仅用于控制管道命令的退出状态码,和管道进程是否阻塞完全无关,因此无法解决该问题。
可落地的解决办法
- 方案1:改造管道命令,主进程结束后主动断开写端
把所有带管道的命令包裹在代码块中,主进程执行完成后主动关闭管道,参考写法:# 以largehistory分支的copyJob调用为例 { /root/scripts/copyJob.sh copyjob copydrive /mnt/backupdrive/ /mnt/copydrive/ # 确保主进程结束后立即关闭写端 exec >&- } | ts '%a %d-%m-%Y %T ' | tee -a /var/log/copybackup.log - 方案2:修改
copyJob.sh的后台进程重定向规则
脚本内所有后台启动的命令(末尾带&的命令)全部添加标准输出、错误的重定向,避免继承父进程的管道文件描述符,参考写法:# 后台进程示例,修改前: some-background-task & # 修改后: some-background-task >/dev/null 2>&1 & # 如果需要保留后台进程日志,可以重定向到独立日志文件 - 方案3:用进程替换替代管道(仅支持Bash环境)
进程替换的特性是主命令执行完成后会自动关闭对应的文件描述符,不会被后台子进程继承,参考写法:ts '%a %d-%m-%Y %T ' < <(/usr/bin/rsnapshot -c /etc/rsnapshot_smallhistory.conf daily) | tee -a /var/log/rsnapshot_smallhistory_verbose.log - 方案4:开启Bash的lastpipe选项
在脚本开头添加shopt -s lastpipe配置,该选项会让管道的最后一个进程在当前Shell环境中执行,主命令结束后会自动回收管道相关资源,避免残留。
内容的提问来源于stack exchange,提问作者Stefan Brunner
相关产品推荐
相关产品推荐

