tail -f 的实现

8 篇文章 0 订阅
最原始的想法和实现

    最容易想到的就是不断的读取一个文件,如果读取到文件结尾(EOF),那么sleep一下然后再次尝试。

    事实上Apache Common IO里就有一个这样的实现

    这种方法到优点是简单而且不需要任何操作系统或者文件系统的支持(跨平台是不会有任何问题),缺点就是如果sleep过长,那么tail -f的结果不是很及时,如果太短,可能浪费到cpu和io很多

    改进的思路很自然,就像io那样,从最原始的cpu轮询到中断的思路。

    就像中断需要更多的硬件支持一样,改进版的tail也需要更多操作系统和文件系统到支持。

网上的相关资料
  1.     GNU coreutils里的实现,也是我们在Linux里用到到命令,其实我们经常用到的命令实现起来也是不容易到,tail.c有两千多行代码。
  2.     JavaEye里INotify的介绍 ,主要是inotify-tools这个工具到介绍,使用这个工具可以实现shell脚本监视文件系统的变化
  3.    Inotify的介绍 ,里面有简洁的示例代码,我后面用c实现的copy了这里到代码。
  4.     http://jnotify.sourceforge.net/  jni实现的跨平台的文件系统监控库,支持win32/64 Linux 和 Mac os(我只测试了Linux,不过它有其它系统到动态链接库文件)
  5.     Java 7的nio.2 ,里面提供了监控目录的api,不过它好像只支持目录的监控,如果要监控文件,需要遍历目录的事件
Linux下的C实现

      主要参考了Inotify的介绍,GNU coreutils到代码太长了,没细看

      核心代码就3行: fd = inotify_init();
wd = inotify_add_watch(fd, argv1, IN_MODIFY);
read(fd, buffer, 1024);

#include <stdio.h>
#include <stdlib.h>
#include <linux/unistd.h>
#include <linux/inotify.h>
#include <errno.h>

int main(int argc, char** argv) {
	char buffer[1024];
	int ch;
	long curFilePointer;
	int fp;
	int fd;
	int wd;
	if(argc!=2){
		printf("usage: tail file-path.\n");
		return EXIT_FAILURE;
	}
	fp=fopen(argv[1],"r");
	if(fp<0){
		printf("can't open %s\n", argv[1]);
		return EXIT_FAILURE;
	}

	fd = inotify_init();
	if (fd < 0) {
		printf("Fail to initialize inotify.\n");
		return EXIT_FAILURE;
	}
	wd = inotify_add_watch(fd, argv[1], IN_MODIFY);
	if (wd < 0) {
		printf("Can't add watch for %s.\n", argv[1]);
		return EXIT_FAILURE;
	}

	while(1){
		while ((ch=fgetc(fp))!=EOF){
			putchar(ch);
			curFilePointer++;
		}
		read(fd, buffer, 1024);
	}

	return EXIT_SUCCESS;
}


JNotify测试

       下载zip解压后有个jar包和本地的dll,比如linux就是libJnotify.so

package test;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import net.contentobjects.jnotify.JNotify;
import net.contentobjects.jnotify.JNotifyException;
import net.contentobjects.jnotify.JNotifyListener;

public class JavaTail {

	/**
	 * @param args
	 * @throws JNotifyException
	 * @throws InterruptedException
	 * @throws FileNotFoundException
	 */
	public static void main(String[] args) throws JNotifyException, IOException, InterruptedException {
		if(args.length!=1){
			System.err.println("Usage: test.JavaTail filePath!");
			return;
		}

		int mask = JNotify.FILE_MODIFIED;
		int watchID = JNotify.addWatch(args[0], mask, false, (JNotifyListener) new Listener(args[0]));
		Thread.sleep(Long.MAX_VALUE);
	}


}

class Listener implements JNotifyListener {
		FileReader fr = null;
		public Listener(String filePath) throws IOException{
			fr=new FileReader(filePath);
			int ch = 0;
			while((ch=fr.read())!=-1){
				System.out.print((char)ch);
			}
		}
	    public void fileRenamed(int wd, String rootPath, String oldName,
	        String newName) {

	    }
	    public void fileModified(int wd, String rootPath, String name) {
			int ch = 0;
			try {
				while((ch=fr.read())!=-1){
					System.out.print((char)ch);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
	    }
	    public void fileDeleted(int wd, String rootPath, String name) {

	    }
	    public void fileCreated(int wd, String rootPath, String name) {

	    }
}

   

Java7 NIO.2的实现

      可以看到只能监控目录到事件,然后变历,判断是不是我们关注到某个文件。

package test;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class JavaTail {

	/**
	 * @param args
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws IOException, InterruptedException {
		if(args.length!=1){
			System.err.println("Usage: test.JavaTail filePath!");
			return;
		}

		File file=new File(args[0]);

		FileReader fr=new FileReader(file);
		int ch = 0;
		while((ch=fr.read())!=-1){
			System.out.print((char)ch);
		}

		File parent=file.getParentFile();
		Path filePath=file.toPath();
		Path dir=parent.toPath();
		WatchService watcher = FileSystems.getDefault().newWatchService();
		WatchKey key=dir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);

		while(true){
			key=watcher.take();
		    for (WatchEvent<?> event: key.pollEvents()) {
		        WatchEvent.Kind<?> kind = event.kind();

		        if (kind ==StandardWatchEventKinds.ENTRY_MODIFY){
			        WatchEvent<Path> ev = (WatchEvent<Path>)event;
			        Path filename = ev.context();
			        Path child = dir.resolve(filename);
			        if(child.equals(filePath)){
						while((ch=fr.read())!=-1){
							System.out.print((char)ch);
						}
			        }
		        }
		    }
		    if(!key.reset()){
		    	System.err.println("key.reset() return false");
		    	break;
		    }
		}
	}

}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值