玩转 SHELL 脚本之:Shell 命令 Buffer 知多少?

1、问题:

下午有同学问了这么一个问题:

tail -n +$(tail -n1 /root/tmp/n) -F /root/tmp/ip.txt 2>&1| awk 'ARGIND==1{i=$0;next}{i++;if($0~/文件已截断/){i=0};print $1"---"i;print i >> "/root/tmp/n"}' /root/tmp/n -  

seq 10 > /root/tmp/ip.txt && tail -f /root/tmp/n

把这两条语句分别在同一台机器的两个终端上执行,你会发现第二条语句的 tail 跟踪不到结果,而第一条语句明明是有结果输出的。

在往下细说之前,咱们先简单介绍下第一个语句干嘛的:

这个语句是实时 tail 一份日志,并实现了两个小功能:

当文件被重写的时候将文件的行号置 0,并且当进程挂掉后,重启进程时,能从上次挂掉的地方开始 tail 起,类似“断点续传”。

不熟悉 awk 的同学看起来估计比较费劲,没关系,咱们简化下场景,写个简单的 test case 模拟上面的语句(1):

{ echo 21;sleep 10;echo 22; }|awk '{print >> "/root/tmp/n"}'

你会发现确实是当屏幕输出了 21 的时候, n 的值没有变化,但是当整个 echo 执行完成时,n 的值却一起变化了,输出了 21、22。

对此,你很容易写出另一个 test case:

while [[ $i -lt 10 ]]; do ((i++)); echo $i|awk '{print >> "/root/tmp/n"}'; sleep 2; done

那为什么这个 case 能实时看到 n 的值在变化呢?别急,读完本文,你自会找到答案。^ _ ^

其实语句(1)的问题在于 shell 下的一个概念引发的:buffer

写过程序的同学应该知道 磁盘与内存,内存与CPU 的 IO 交互速度都不在一个量级上,那么为了提高数据的存取效率,一般都会在软件程序、硬件设计中采用 buffer 的设计,当 buffer 满了才会请求一次 IO 操作,而不是一个字符或者一个字节的方式请求 IO 操作,具体说来一般是交互式的会无 buffer 或者小 buffer,非交互式的操作一般 buffer 都会比较大,因为对用户来说“实时性”要求不是那么高了嘛~

语句 command1 | command2 大体的流程图如下:

例如如下语句的流程图如下:

tail -f access.log | cut -d' ' -f1 | uniq

语句(1) 的重定向就是一个典型的非交互式操作,会由于 buffer 的原因,用户无法实时的看到日志中数据的变化。

2、解决方案:

知道原因了,咱们可以有如下几种方式,让 awk 中的重定向变得无 buffer 实时输出:

{ echo 21;sleep 10;echo 22; }|awk '{print >> "/root/tmp/n"; fflush("")}'
{ echo 21;sleep 10;echo 22; }|awk '{print >> "/root/tmp/n"; system("")}'
{ echo 21;sleep 10;echo 22; }|awk '{print >> "/root/tmp/n"; close("/root/tmp/n")}'
{ echo 21;sleep 10;echo 22; }|awk '{system("echo "$0" >> /root/tmp/n")}'
{ echo 21;sleep 10;echo 22; }|awk '{print |"cat - >> /root/tmp/n"}'

关于 fflush 的说明如下:

fflush([file]) Flush any buffers associated with the open output file or pipe file. If file is missing, then standard output is flushed. If file is the null string, then all open output files and pipes have their buffers flushed.

说道这儿,有同学或许会有疑问:还有什么办法去验证是 buffer 的原因呢?

其实你调大你的输出就行了:

{ seq 5000;sleep 10;seq 1000; }|awk '{print >> "/root/tmp/n"}'

3、推而广之

其实 linux shell 下的众多命令都采用了 buffer 的设计,例如 grep,比如就曾经有同学问过我:

tail -f logfile | grep 'ooxx' 为什么看不到结果呢?日志中明明就有的呀? 等等。。。

那本文在此稍稍总结下常用命令的 buffer 问题以及应对措施:

grep (e.g. GNU version 2.5.1)

--line-buffered

sed (e.g. GNU version 4.0.6)

-u,--unbuffered

awk (GNU awk)

use the fflush() function

awk (mawk)

-W interactive

tcpdump, tethereal

-l

例如上文提到的 grep buffer 问题:

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

也有专门的命令或者工具包来解决这个问题,比如 unbuffer、stdbuf,或者直接调用 c 语言库禁用 buffer:

setvbuf(stdout, 0, _IONBF, 0);

4、Refer:

[1] 9.1.4 Input/Output Functions

https://www.gnu.org/software/gawk/manual/html_node/I_002fO-Functions.html

[2] What is buffering? Or, why does my command line produce no output: tail -f logfile | grep 'foo bar' | awk ...

http://mywiki.wooledge.org/BashFAQ/009

[3] How to fix stdio buffering

http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html

[4] buffering in standard streams

http://www.pixelbeat.org/programming/stdio_buffering/

[5] 关于awk中通过管道执行shell后的管道关闭问题

http://hi.baidu.com/leejun_2005/item/26a5f8273e7e3555c28d5970

[6] Why does awk do full buffering when reading from a pipe

http://unix.stackexchange.com/questions/33650/why-does-awk-do-full-buffering-when-reading-from-a-pipe

[7] tailf and tail -f

http://blogread.cn/it/article/6892?f=wb

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏崔庆才的专栏

对你没有看错!不到 10 行代码完成抖音热门视频的爬取!

最近研究了一下抖音的爬虫,目前实现了热门话题和热门音乐下面所有相关视频的爬取,并且我已经将该爬虫打包成了一个 Python 库并发布,名称就叫做 douyin,...

2513
来自专栏Python中文社区

Python开源项目介绍:网站日志分析工具

日志分析在web系统中故障排查、性能分析方面有着非常重要的作用。该工具的侧重点不是通常的PV,UV等展示,而是在指定时间段内提供细粒度(最小分钟级别,即一分钟内...

1773
来自专栏Java职业技术分享

并发模型比较

Golang 的特色之一就是 goroutine ,使得程序员进行并发编程更加方便,适合用来进行服务器编程。作为后端开发工程师,有必要了解并发编程面临的场景和常...

3760
来自专栏杨建荣的学习笔记

使用sysbench压力测试MySQL(二)

昨天有了第一篇的测试之后,仅仅是一个开始。 我接下来做sysbench压测的主要思路是根据现有的配置作出调整,能够持续性的优化和压力测试达到目的,而...

8179
来自专栏SeanCheney的专栏

Awesome Asyncio 《碉堡的Asyncio·中文版》Awesome-Asyncio-CN

Awesome-asyncio 是 Timo Furrer 发起并维护的 Python Asyncio 资源列表。本项目是其中文版,在这里,收集了大量的 Asy...

5464
来自专栏木头编程 - moTzxx

微信公众平台开发[2] —— 微信端分享功能

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/de...

7352
来自专栏Java职业技术分享

并发模型比较

Golang 的特色之一就是 goroutine ,使得程序员进行并发编程更加方便,适合用来进行服务器编程。作为后端开发工程师,有必要了解并发编程面临的场景和常...

2330
来自专栏phodal

编辑-发布-开发分离:git作为NoSQL数据库

动态网页是下一个要解决的难题。我们从数据库中读取数据,再用动态去渲染出一个静态页面,并且缓存服务器来缓存这个页面。既然我们都可以用Varnish、Squid这样...

20910
来自专栏MongoDB中文社区

完美数据迁移-MongoDB Stream的应用

尽管如此,目前还是有许多企业踏上了服务化改造的道路,这其中则免不了”旧改”的各种繁杂事。

1162
来自专栏phodal

如何设计完善的构建系统,为日常开发提速一倍

在搭建开发环境的构建系统时,我们关注二点: 提高效率,对于大部分事务的自动化,如自动编译代码、自动重启服务。 代码质量,编码完成时,我们则转而关注于代码的质量。...

1987

扫码关注云+社区

领取腾讯云代金券