【问题标题】:How to 'grep' a continuous stream?如何“grep”一个连续的流?
【发布时间】:2011-11-01 23:21:35
【问题描述】:

是否可以在连续流上使用grep

我的意思是一种tail -f <file> 命令,但在输出中使用grep 以便只保留我感兴趣的行。

我尝试过tail -f <file> | grep pattern,但似乎grep 只能在tail 完成后执行,也就是说永远不会。

【问题讨论】:

  • 生成文件的程序很可能没有刷新其输出。
  • tail -f file 有效(我实时看到新输出)
  • @Luc 确实,没想到
  • 您的输入流中可能没有新行吗?如果是这样,grep 将不会继续。

标签: linux bash shell grep tail


【解决方案1】:

在使用 BSD grep(FreeBSD、Mac OS X 等)时打开grep 的行缓冲模式

tail -f file | grep --line-buffered my_pattern

似乎不久前--line-buffered 对 GNU grep(几乎在任何 Linux 上使用)并不重要,因为它默认刷新(YMMV 用于其他类 Unix,如 SmartOS、AIX 或 QNX)。但是,截至 2020 年 11 月,需要 --line-buffered(至少在 openSUSE 中使用 GNU grep 3.5,但基于下面的 cmets 似乎通常需要)。

【讨论】:

  • @MichaelNiemand 你可以使用tail -F file | grep --line-buffered my_pattern
  • @MichaelGoldshteyn 放轻松。人们赞成它,因为他们在谷歌搜索“grep line buffered”时发现了这个页面,它为他们解决了一个可能不完全是问题的问题。
  • 我来到这里试图 grep strace 的输出。没有--line-buffered,它将无法工作。
  • @MichaelGoldshteyn(以及他评论的支持者):我一直对tail -f | grep 有这个问题,--line-buffered 为我解决了这个问题(在 Ubuntu 14.04 上,GNU grep 版本 2.16)。 “如果标准输出是 tty,则使用行缓冲”逻辑在哪里实现?在git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c 中,line_buffered 仅由参数解析器设置。
  • @MichaelGoldshteyn 我在 macOS 上使用 BSD grep 并且没有 --line-buffered 我没有输出。但是,经过测试,GNU grep 看起来像您描述的那样。所以像大多数 Unix 一样,它取决于你平台的实现。由于问题没有指定平台,your 信息似乎是错误的 - 在查看 BSD grep 的代码并将其与 GNU grep 进行比较后,该行为肯定由 --line-buffered 选项控制。只是默认情况下只有 GNU grep 刷新。
【解决方案2】:

我一直使用tail -f <file> | grep <pattern>

它会等到 grep 刷新,而不是完成(我使用的是 Ubuntu)。

【讨论】:

  • 这可能会持续很长时间,所以尽量不要急躁。
  • 大概需要多长时间?
  • @Matthieu:主要取决于您的 grep 内容以及操作系统上的缓冲区大小。如果 grep 每隔几个小时只匹配一个短字符串,那么它将在第一次刷新前几天。
  • Tail 不使用输出缓冲 - grep 使用。
  • 不,当输出到 tty 设备时,grep 不会进行输出缓冲,这显然是在这个答案中。它确实行缓冲!这是正确的答案,应该是公认的答案。有关详细信息,请参阅我对当前接受的(错误)答案的较长评论。
【解决方案3】:

我认为您的问题是 grep 使用了一些输出缓冲。试试

tail -f file | stdbuf -o0 grep my_pattern

它将grep的输出缓冲模式设置为无缓冲。

【讨论】:

  • 而且这样做的好处是除了grep之外,它还可以用于许多其他命令。
  • 然而,正如我在玩了更多之后发现的那样,一些命令只有在连接到 tty 时才会刷新它们的输出,为此,unbuffer(在 debian 上的 expect-dev 包中)是国王。所以我会在 stdbuf 上使用 unbuffer。
  • @Peter V. Mørch 是的,你是对的,unbuffer 有时可以在 stdbuf 不能工作的地方工作。但是我认为您正在尝试找到一个“魔术”程序,该程序将始终解决您的问题,而不是理解您的问题。创建虚拟 tty 是不相关的任务。 Stdbuf 完全符合我们的要求(设置标准输出缓冲区以提供值),而 unbuffer 做了很多我们可能不想要的隐藏内容(比较交互式 top 与 stdbuf 和 unbuffer)。而且确实没有“神奇”的解决方案:unbuffer 有时也会失败,例如 awk 使用不同的缓冲区实现(stdbuf 也会失败)。
  • “但我认为您正在尝试找到一个能够始终解决您的问题而不是理解您的问题的'神奇'程序。” - 我想你是正确的! ;-)
  • 更多关于stdbuf、`unbuffer 和 stdio 缓冲的信息pixelbeat.org/programming/stdio_buffering
【解决方案4】:

如果您想在 整个 文件(不仅仅是尾部)中查找匹配项,并且希望它等待任何新的匹配项,这很好用:

tail -c +0 -f <file> | grep --line-buffered <pattern>

-c +0 标志表示输出应从文件的开头 (+) 开始 0 字节 (-c)。

【讨论】:

    【解决方案5】:

    在大多数情况下,您可以tail -f /var/log/some.log |grep foo,它会正常工作。

    如果您需要在运行的日志文件上使用多个 grep 并且发现没有输出,您可能需要将 --line-buffered 开关粘贴到您的 middle grep(s) 中,例如所以:

    tail -f /var/log/some.log | grep --line-buffered foo | grep bar
    

    【讨论】:

      【解决方案6】:

      您可以将此答案视为增强.. 通常我正在使用

      tail -F <fileName> | grep --line-buffered  <pattern> -A 3 -B 5
      

      -F 在文件旋转的情况下更好(如果文件旋转,-f 将无法正常工作)

      -A 和 -B 对于在模式出现之前和之后获取行很有用..这些块将出现在虚线分隔符之间

      但对我来说,我更喜欢做以下事情

      tail -F <file> | less
      

      如果您想在流式日志中搜索,这非常有用。我的意思是前后左右深入观察

      【讨论】:

      • grep -C 3 &lt;pattern&gt;,如果 N 相同,则替换 -A 和 -B
      【解决方案7】:

      没有看到任何人为此提供我通常的选择:

      less +F <file>
      ctrl + c
      /<search term>
      <enter>
      shift + f
      

      我更喜欢这个,因为您可以随时使用ctrl + c 停止并浏览文件,然后只需点击shift + f 即可返回实时流式搜索。

      【讨论】:

        【解决方案8】:

        sed 会是更好的选择( 编辑器)

        tail -n0 -f &lt;file&gt; | sed -n '/search string/p'

        然后,如果您希望在找到特定字符串后退出 tail 命令:

        tail --pid=$(($BASHPID+1)) -n0 -f &lt;file&gt; | sed -n '/search string/{p; q}'

        显然是一个bashism:$BASHPID 将是tail 命令的进程ID。 sed 命令在管道中的 tail 之后,因此 sed 进程 ID 将为 $BASHPID+1。

        【讨论】:

        • 在系统上启动的下一个进程 ($BASHPID+1) 将是你的假设在许多情况下是错误的,这无助于解决可能是 OP 试图解决的缓冲问题问一下。特别是,在这里推荐sed 而不是grep 似乎只是一个(可疑的)偏好问题。 (如果这是您试图传达的重点,您可以使用 grep -m 1 获得 p;q 行为。)
        • 有效,sed 命令在准备好后立即打印每一行,--line-buffered 的 grep 命令没有。我真的不明白负1。
        • 目前已经确定缓冲是 grep 的问题。使用sed 处理行缓冲不需要特殊操作,这是默认行为,因此我强调stream 这个词。确实,没有保证 $BASHPID+1 将是正确的pid,但是由于pid 分配is sequential 并且管道命令被分配了一个 pid 紧随其后,很有可能。
        【解决方案9】:

        是的,这实际上可以正常工作。 Grep 和大多数 Unix 命令一次在流上运行一行。 tail 出来的每一行都将被分析并在匹配时传递。

        【讨论】:

        • 这实际上是不正确的。如果grep 是管道链中的最后一个命令,它将按照您的解释执行。但是,如果它在中间,它将一次缓冲大约 8k 输出。
        【解决方案10】:

        这个命令对我有用(Suse):

        mail-srv:/var/log # tail -f /var/log/mail.info |grep --line-buffered LOGIN  >> logins_to_mail
        

        收集邮件服务的登录信息

        【讨论】:

          【解决方案11】:

          这个问题迟到了,考虑到这种工作是监控工作的重要组成部分,这是我的(不是那么短)答案...

          使用 跟踪日志

          1。命令tail

          这个命令比在已经发布的答案上阅读的要完整一些

          1. follow 选项 tail -ftail -F 之间的区别,来自 manpage

               -f, --follow[={name|descriptor}]
                      output appended data as the file grows;
            ...
               -F     same as --follow=name --retry
            ...
               --retry
                      keep trying to open a file if it is inaccessible
            

            这意味着:通过使用 -F 而不是 -ftail 将在删除时重新打开文件(在日志轮换中,示例)。
            这对于多日观看日志文件很有用。

          2. 能够同时关注多个文件
            我已经用过:

            tail -F /var/www/clients/client*/web*/log/{error,access}.log /var/log/{mail,auth}.log \
                       /var/log/apache2/{,ssl_,other_vhosts_}access.log \
                       /var/log/pure-ftpd/transfer.log
            

            通过数百个文件跟踪事件...(考虑此答案的其余部分以了解如何使其可读...;)

          3. 使用开关 -n(不要使用 -c 进行行缓冲!)。
            默认情况下,tail 将显示最后 10 行。这可以调整:

            tail -n 0 -F file
            

            将跟随文件,但只会打印新行

            tail -n +0 -F file
            

            在跟随他的进展之前将打印整个文件。

          2。 管道时的缓冲区问题:

          如果您打算过滤输出,请考虑缓冲!请参阅 -u 选项以获取 sed--line-buffered 以获取 grepstdbuf 命令:

          tail -F /some/files | sed -une '/Regular Expression/p'
          

          与在sed 命令中不使用-u 开关相比,它(比使用grep 更有效)更具反应性。

          tail -F /some/files |
              sed -une '/Regular Expression/p' |
              stdbuf -i0 -o0 tee /some/resultfile
          

          3。最近的日志系统

          在最近的系统上,您必须以几乎相同的方式运行 journalctl -xf 而不是 tail -f /var/log/syslog...

          journalctl -axf | sed -une '/Regular Expression/p'
          

          但是请阅读man page,这个工具是为日志分析而构建的!

          4。将此集成到 脚本中

          1. 两个(或更多)文件的彩色输出

            这是一个监视许多文件的脚本示例,第一个文件的输出颜色与其他文件不同:

            #!/bin/bash
            
            tail -F "$@" |
                sed -une "
                    /^==> /{h;};
                    //!{
                        G;
                        s/^\\(.*\\)\\n==>.*${1//\//\\\/}.*<==/\\o33[47m\\1\\o33[0m/;
                        s/^\\(.*\\)\\n==> .* <==/\\o33[47;31m\\1\\o33[0m/;
                        p;}"
            

            它们在我的主机上运行良好,正在运行:

            sudo ./myColoredTail /var/log/{kern.,sys}log
            
          2. 交互脚本

            您可能正在查看日志以对事件做出反应?

            这是一个小脚本,当某个 USB 设备出现或消失时播放一些声音,但相同的脚本可以发送邮件,或任何其他交互,例如打开咖啡机电源...

            #!/bin/bash
            
            exec {tailF}< <(tail -F /var/log/kern.log)
            tailPid=$!
            
            while :;do
                read -rsn 1 -t .3 keyboard
                [ "${keyboard,}" = "q" ] && break
                if read -ru $tailF -t 0 _ ;then
                    read -ru $tailF line
                    case $line in
                        *New\ USB\ device\ found* ) play /some/sound.ogg ;;
                        *USB\ disconnect* ) play /some/othersound.ogg ;;
                    esac
                    printf "\r%s\e[K" "$line"
                fi
            done
            
            echo
            exec {tailF}<&-
            kill $tailPid
            

            您可以按Q退出

          【讨论】:

          • 优秀而详尽的答案。谢谢
          【解决方案12】:

          你肯定不会成功

          tail -f /var/log/foo.log |grep --line-buffered string2search
          

          当您使用“colortail”作为尾巴的别名时,例如。在 bash 中

          alias tail='colortail -n 30'
          

          你可以通过 类型别名 如果这输出类似 tail 是colortail -n 30 的别名。 那么你有罪魁祸首:)

          解决方案:

          删除别名

          unalias tail
          

          通过此命令确保您使用的是“真正的”尾部二进制文件

          type tail
          

          应该输出如下内容:

          tail is /usr/bin/tail
          

          然后你就可以运行你的命令了

          tail -f foo.log |grep --line-buffered something
          

          祝你好运。

          【讨论】:

            【解决方案13】:

            在没有行缓冲选项的地方使用 awk(另一个很棒的 bash 实用程序)而不是 grep!它会不断地从尾部流式传输您的数据。

            这就是你使用 grep 的方式

            tail -f <file> | grep pattern
            

            这就是你将如何使用 awk

            tail -f <file> | awk '/pattern/{print $0}'
            

            【讨论】:

            • 这是不正确的;开箱即用的 awk 执行行缓冲,就像大多数其他标准 Unix 工具一样。 (此外,{print $0} 是多余的,因为打印是条件通过时的默认操作。)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-12-05
            • 1970-01-01
            • 2021-04-04
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多