tail——跟踪多个文件并保留原子行

tail——跟踪多个文件并保留原子行

我想跟踪多个日志文件并将传入的行输出到单个管道。但是,不加思索地这样做(例如tail -F)可能会产生断行:例如,两个日志中的行ABC\nXYZ\n可能会混淆并变成ABXYZ\nC\n

以下是一个例子:

$ >a >b
$ (echo -n a >>a; sleep 2; echo A >>a) &
$ (echo -n b >>b; sleep 2; echo B >>b) &
$ tail -Fq a b

理想情况下,这会产生aA\nbB\n。实际上,abA\nB\n会产生类似的东西。

我怎样才能输出这些行而不让它们混淆?

以下是我尝试过的一些方法

  • 我没有使用单个tail -Fq,而是尝试tail为每个文件使用单独的实例:

    $ (trap 'kill 0' EXIT; tail -F a & tail -F b & wait)
    

    不过,我认为这只是将问题转移tail到管道缓冲区,问题并没有得到解决。

  • 使用单独的实例并用于grep缓冲每一行。

    $ (trap 'kill 0' EXIT; tail -F a | grep -F '' & tail -F b | grep -F '' & wait)
    

    这似乎有效。但是,我不确定这有多持久。我认为它具有与此问题中讨论的相同的限制:写单行时 echo 是原子的吗

    (此外,有没有更好的方法来完成grep -F ''这里的事情?)

答案1

这是一个无需安装额外程序的解决方案,例如多尾

它与问题的第二个示例非常相似,但要使用的命令是grep -F '' --line-bufferedgrep -F这里可以简称为fgrep。关于grep -F/fgrep与普通正则表达式 grep,使用固定字符串 grep 是轻微地比用于相同目的的方法更快grep ^ --line-buffered

综合起来,多行版本如下:

(
trap 'kill 0' EXIT
tail -F a | fgrep '' --line-buffered &
tail -F b | fgrep '' --line-buffered &
wait
)

如果要将其放入 shell 脚本中,则可能不需要子 shell ( )。要将其变成一行,请删除换行符,并在;不以 & 符号 ( ) 结尾的行末尾添加分号 ( &)。

深入解决方案

这实际上解决了两个问题:

首先,tail -F它会在看到文件时使用并输出文件中的字节,而不等待行尾。这就是现状,tail目前无法改变这一点。因此,我们无法做到这一点,tail -Fq a b而必须为每个文件使用单独的进程。

其次,在每个文件上执行完之后tail -F,问题仍然存在,即输出可能会在管道缓冲区中混淆。由于tail刷新是针对任意字节而不是针对整行,因此有充分的理由出现这种情况。指定stdbuf -oL到行缓冲区tail不会改变这一点,因为 tail 似乎会覆盖这一点。

为了解决第二个问题,我们需要使用类似grep等待整行后再输出的方法。此外,我们需要指定,--line-buffered否则 grep 本身将缓冲其输出并在缓冲区已满时刷新,这可能不在行边界。

各种各样的

为了解释trap 'kill 0' EXIT...的作用,当使用类似-之类的命令来中断时wait,需要防止tail -F进程被抛在后面。CtrlC

相关内容