使用 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,但根据下面的评论似乎通常需要它)。
我一直使用 tail -f <file> | grep <pattern>
。
它会等到 grep 刷新,而不是完成(我使用的是 Ubuntu)。
我认为您的问题是 grep 使用了一些输出缓冲。尝试
tail -f file | stdbuf -o0 grep my_pattern
它将grep的输出缓冲模式设置为无缓冲。
grep
之外的许多其他命令。
unbuffer
(在 debian 的 expect-dev
包中)是 king。所以我会在 stdbuf 上使用 unbuffer。
top
与 stdbuf 和 unbuffer)。而且确实没有“神奇”的解决方案:unbuffer 有时也会失败,例如 awk 使用不同的缓冲区实现(stdbuf 也会失败)。
stdbuf
、`unbuffer 和 pixelbeat.org/programming/stdio_buffering 处的 stdio 缓冲的更多信息
如果您想在整个文件中查找匹配项(而不仅仅是尾部),并且您希望它坐下来等待任何新的匹配项,这很好用:
tail -c +0 -f <file> | grep --line-buffered <pattern>
-c +0
标志表示输出应从文件的开头 (+
) 开始 0
个字节 (-c
)。
在大多数情况下,您可以 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
您可以将此答案视为增强功能..通常我正在使用
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 相同。
没有看到任何人为此提供我通常的选择:
less +F <file>
ctrl + c
/<search term>
<enter>
shift + f
我更喜欢这个,因为您可以随时使用 ctrl + c
停止并浏览文件,然后只需点击 shift + f
即可返回实时流式搜索。
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
行为。)
--line-buffered
的 grep 命令则没有。我真的不明白负1。
是的,这实际上可以正常工作。 Grep
和大多数 Unix 命令一次在流上运行一行。从尾部出来的每一行都将被分析并在匹配时传递。
grep
是管道链中的最后一个命令,它将按照您的解释执行。但是,如果它在中间,它将一次缓冲大约 8k 输出。
在这个问题上有些迟到,考虑到这种工作是监控工作的重要组成部分,这是我(不是那么短)的答案......
使用 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-buffered
或 stdbuf
命令:
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键退出。
这个命令对我有用(Suse):
mail-srv:/var/log # tail -f /var/log/mail.info |grep --line-buffered LOGIN >> logins_to_mail
收集邮件服务的登录信息
你肯定不会成功
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
祝你好运。
在没有行缓冲选项的地方使用 awk(另一个很棒的 bash 实用程序)而不是 grep!它会不断地从尾部流式传输您的数据。
这就是你使用 grep 的方式
tail -f <file> | grep pattern
这就是你将如何使用 awk
tail -f <file> | awk '/pattern/{print $0}'
{print $0}
是多余的,因为打印是条件通过时的默认操作。)
strace
的输出。没有--line-buffered
,它将无法工作。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
仅由参数解析器设置。--line-buffered
我没有得到任何输出。但是,经过测试,GNU grep 看起来像您描述的那样。所以像大多数 Unix 一样,它取决于你平台的实现。由于问题没有指定平台,your 信息似乎是错误的 - 在查看 BSD grep 的代码并将其与 GNU grep 进行比较后,该行为肯定由 --line-buffered 选项控制。只是默认情况下只有 GNU grep 刷新。