下午有同窗问了这么一个问题: html
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 跟踪不到结果,而第一条语句明明是有结果输出的。
在往下细说以前,我们先简单介绍下第一个语句干吗的: node
这个语句是实时 tail 一份日志,并实现了两个小功能: linux
当文件被重写的时候将文件的行号置 0,而且当进程挂掉后,重启进程时,能从上次挂掉的地方开始 tail 起,相似“断点续传”。 shell
不熟悉 awk 的同窗看起来估计比较费劲,不要紧,我们简化下场景,写个简单的 test case 模拟上面的语句(1): tcp
{ echo 21;sleep 10;echo 22; }|awk '{print >> "/root/tmp/n"}'你会发现确实是当屏幕输出了 21 的时候, n 的值没有变化,可是当整个 echo 执行完成时,n 的值却一块儿变化了,输出了 2一、22。
对此,你很容易写出另外一个 test case: 工具
while [[ $i -lt 10 ]]; do ((i++)); echo $i|awk '{print >> "/root/tmp/n"}'; sleep 2; done那为何这个 case 能实时看到 n 的值在变化呢?别急,读完本文,你自会找到答案。^ _ ^
其实语句(1)的问题在于 shell 下的一个概念引起的:buffer post
写过程序的同窗应该知道 磁盘与内存,内存与CPU 的 IO 交互速度都不在一个量级上,那么为了提升数据的存取效率,通常都会在软件程序、硬件设计中采用 buffer 的设计,当 buffer 满了才会请求一次 IO 操做,而不是一个字符或者一个字节的方式请求 IO 操做,具体说来通常是交互式的会无 buffer 或者小 buffer,非交互式的操做通常 buffer 都会比较大,由于对用户来讲“实时性”要求不是那么高了嘛~ spa
语句 command1 | command2 大致的流程图以下: 设计
例如以下语句的流程图以下: unix
tail -f access.log | cut -d' ' -f1 | uniq
语句(1) 的重定向就是一个典型的非交互式操做,会因为 buffer 的缘由,用户没法实时的看到日志中数据的变化。
知道缘由了,我们能够有以下几种方式,让 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"}'
其实 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);
[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
[7] tailf and tail -f