如何终止Linux tee命令而不杀死它从其接收的应用程序


19

我有一个bash脚本,只要Linux机器通电就可以运行。我按如下所示启动它:

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

启动后,我可以在ps输出中看到tee命令,如下所示:

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

我有一个功能可以监视tee生成的日志的大小,并在日志达到一定大小时杀死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
}

令我惊讶的是,杀死tee命令也会被start.sh实例杀死。为什么是这样?如何结束tee命令,但要继续运行start.sh?谢谢。

Answers:


34

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

如果您修改脚本以捕获SIGPIPE并采取一些适当的措施(例如,停止编写输出),那么在tee终止后,应该可以继续执行脚本。


更好的是,而不是完全取消tee 使用logrotatecopytruncate选项可以简化操作。

引用logrotate(8)

copytruncate

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


9
您还需要使用tee -atee以附加模式打开文件,否则,Tee将在截断文件后以相同的偏移量继续写入文件(以及在不支持稀疏文件的系统(如macOS)上)重新分配导致该位置的文件部分,占用磁盘空间的两倍。
斯特凡Chazelas

4
另一种选择是通过管道传输logger -ssyslog以处理日志记录(-s也可以在stderr上打印)。
斯特凡Chazelas

1
为+1 logrotate。很棒的节目
德米特里·库德里亚夫采夫

2
或在使用systemd和日记的systemd-cat而不是logger的系统上。然后,您可以免费获得许多输出过滤和旋转。
Zan Lynx

3

解释“为什么”

简而言之:如果写入失败没有导致程序退出(默认情况下),我们将陷入困境。考虑find . | head -n 10- findhead已经占用了所需的10行并继续进行之后,您不想继续运行,扫描硬盘的其余部分。

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

考虑以下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

...这首先是将追加到/tmp/nginx/debug_log,将文件重命名/tmp/nginx/debug_log.old为存在超过100KB的内容。由于记录器本身在进行旋转,因此旋转时不会出现管道破裂,错误且没有数据丢失窗口的情况-每行将被写入一个文件或另一个文件。

当然,在本机bash中实现此方法效率不高,但是以上只是一个示例。有许多程序可以为您实现上述逻辑。考虑:

  • svlogd,来自Runit套件的服务记录器。
  • s6-log,是skanet套件中积极维护的替代方案。
  • multilog 来自DJB Daemontools,是该过程监督和监视工具系列的祖父。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.