ChatGPT解决这个技术问题 Extra ChatGPT

如何“grep”一个连续的流?

可以在连续流上使用 grep 吗?

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

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

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

c
ciobi

使用 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,但根据下面的评论似乎通常需要它)。


@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 刷新。
I
Irit Katriel

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

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


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

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

tail -f file | stdbuf -o0 grep my_pattern

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


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

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

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

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


D
Dale Anderson

在大多数情况下,您可以 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

m
mebada

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

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

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

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

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

tail -F <file> | less

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


grep -C 3 <pattern>,替换 -A <N> -B<N>如果 N 相同。
H
Hans.Loven.work

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

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

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


C
Christian Herr

sed 将是一个更好的选择(流编辑器)

tail -n0 -f <file> | sed -n '/search string/p'

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

tail --pid=$(($BASHPID+1)) -n0 -f <file> | 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 紧随其后,这是完全有可能的。
C
Caleb

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


这实际上是不正确的。如果 grep 是管道链中的最后一个命令,它将按照您的解释执行。但是,如果它在中间,它将一次缓冲大约 8k 输出。
F
F. Hauri - Give Up GitHub

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

使用 bash 跟踪日志

1. 命令尾

这个命令比阅读已经发布的答案更完整

跟随选项 tail -f 和 tail -F 之间的区别,来自手册页: -f, --follow[={name|descriptor}] 随着文件的增长输出附加数据; ... -F 等同于 --follow=name --retry ... --retry 如果文件不可访问,则继续尝试打开文件 这意味着:通过使用 -F 而不是 -f,tail 将重新打开文件( s) 移除时(在日志轮换中,用于示例)。这对于多日观看日志文件很有用。我已经使用过同时跟踪多个文件的能力: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 通过数百个文件跟踪事件......(考虑这个答案的其余部分以了解如何使其可读... ;) 使用开关 -n (不要使用 -c 进行行缓冲!)。默认情况下,tail 将显示最后 10 行。这可以调整: tail -n 0 -F file 将跟随文件,但只会打印新行 tail -n +0 -F file 将在跟随他的进程之前打印整个文件。

2.管道时的缓冲问题:

如果您打算过滤输出,请考虑缓冲!请参阅 sed-u 选项、grep--line-bufferedstdbuf 命令:

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. 将其集成到 bash 脚本中

两个文件(或更多)的彩色输出 下面是一个监视许多文件的脚本示例,第一个文件的输出颜色与其他文件不同:#!/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 交互式脚本 您可能正在观看日志对事件做出反应?这是一个小脚本在某些 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* ) 播放 /some/sound.ogg ;; *USB\ 断开连接* ) 播放 /some/othersound.ogg ;; esac printf "\r%s\e[K" "$line" fi done echo exec {tailF}<&- kill $tailPid 你可以按Q键退出。


优秀而详尽的答案。谢谢
u
user10584393

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

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

收集邮件服务的登录信息


u
user882786

你肯定不会成功

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

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

alias tail='colortail -n 30'

您可以通过类型别名检查这是否输出类似于 colortail -n 30 的尾部 isan 别名。那么你有你的罪魁祸首:)

解决方案:

删除别名

unalias tail

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

type tail

它应该输出如下内容:

tail is /usr/bin/tail

然后你可以运行你的命令

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

祝你好运。


A
Atif

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

这就是你使用 grep 的方式

tail -f <file> | grep pattern

这就是你将如何使用 awk

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

这是不正确的;开箱即用的 awk 执行行缓冲,就像大多数其他标准 Unix 工具一样。 (此外,{print $0} 是多余的,因为打印是条件通过时的默认操作。)