如何终止 Linux tee 命令而不终止从中接收的应用程序

如何终止 Linux tee 命令而不终止从中接收的应用程序

我有一个只要 Linux 机器开机就运行的 bash 脚本。我启动它,如下所示:

( /mnt/apps/start.sh 2>&1 | tee /tmp/nginx/debug_log.log ) &

启动后,我可以在我的窗口中看到 tee 命令附注输出如下图:

$ ps | grep tee
  418 root       0:02 tee /tmp/nginx/debug_log.log
3557 root       0:00 grep tee

我有一个功能可以监视日志的大小球座产生并杀死球座当日志达到一定大小时命令:

monitor_debug_log_size() {
                ## Monitor the file size of the debug log to make sure it does not get too big
                while true; do
                                cecho r "CHECKING DEBUG LOG SIZE... "
                                debugLogSizeBytes=$(stat -c%s "/tmp/nginx/debug_log.log")
                                cecho r "DEBUG LOG SIZE: $debugLogSizeBytes"
                                if [ $((debugLogSizeBytes)) -gt 100000 ]; then
                                                cecho r "DEBUG LOG HAS GROWN TO LARGE... "
                                                sleep 3
                                                #rm -rf /tmp/nginx/debug_log.log 1>/dev/null 2>/dev/null
                                                kill -9 `pgrep -f tee`
                                fi
                                sleep 30
                done
}

令我惊讶的是,杀死了球座命令还通过 start.sh 实例杀死。为什么是这样?我怎样才能结束球座命令但我的 start.sh 继续运行吗?谢谢。

答案1

tee终止时,提供给它的命令将继续运行,直到它尝试写入更多输出。然后它将收到一个 SIGPIPE(在大多数系统上为 13),用于尝试写入没有读取器的管道。

如果您修改脚本以捕获 SIGPIPE 并采取一些适当的操作(例如,停止写入输出),那么您应该能够在 tee 终止后继续它。


比杀戮更好tee 无论如何,logrotate为简单起见,与选项一起使用copytruncate

去引用logrotate(8):

copytruncate

创建副本后就地截断原始日志文件,而不是移动旧日志文件并选择创建新日志文件。当某些程序无法被告知关闭其日志文件并因此可能继续永远写入(追加)到前一个日志文件时,可以使用它。请注意,复制文件和截断文件之间的时间片非常短,因此可能会丢失一些日志记录数据。使用此选项时,创建选项将不起作用,因为旧日志文件保留在原处。

答案2

解释“为什么”

简而言之:如果写入失败没有导致程序退出(默认情况下),我们就会陷入混乱。考虑一下——在已经获取了所需的 10 行并继续之后find . | head -n 10,您不想find继续运行、扫描硬盘驱动器的其余部分。head

做得更好:在记录仪内部旋转

考虑以下tee根本不使用的示例:

#!/usr/bin/env bash

file=${1:-debug.log}                     # filename as 1st argument
max_size=${2:-100000}                    # max size as 2nd argument
size=$(stat --format=%s -- "$file") || exit  # Use GNU stat to retrieve size
exec >>"$file"                           # Open file for append

while IFS= read -r line; do              # read a line from stdin
  size=$(( size + ${#line} + 1 ))        # add line's length + 1 to our counter
  if (( size > max_size )); then         # and if it exceeds our maximum...
    mv -- "$file" "$file.old"            # ...rename the file away...
    exec >"$file"                        # ...and reopen a new file as stdout
    size=0                               # ...resetting our size counter
  fi
  printf '%s\n' "$line"                  # regardless, append to our current stdout
done

如果运行为:

/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log

...这将通过追加到 开始,当内容超过 100KB 时/tmp/nginx/debug_log将文件重命名为。/tmp/nginx/debug_log.old由于记录器本身正在执行轮换,因此在轮换发生时不会出现管道损坏、错误和数据丢失窗口 - 每一行都会写入一个文件或另一个文件。

当然,在本机 bash 中实现这一点效率很低,但上面是一个说明性示例。有许多可用的程序可以为您实现上述逻辑。考虑:

  • svlogd,Runit 套件中的服务记录器。
  • s6-log,来自 skanet 套件的一个积极维护的替代方案。
  • multilog来自 DJB Daemontools,该流程监督和监控工具系列的鼻祖。

相关内容