java tail -f 后返回_tail -f 的实现 | 学步园
最原始的想法和實現
最容易想到的就是不斷的讀取一個文件,如果讀取到文件結尾(EOF),那么sleep一下然后再次嘗試。
事實上Apache Common IO里就有一個這樣的實現
這種方法到優點是簡單而且不需要任何操作系統或者文件系統的支持(跨平臺是不會有任何問題),缺點就是如果sleep過長,那么tail -f的結果不是很及時,如果太短,可能浪費到cpu和io很多
改進的思路很自然,就像io那樣,從最原始的cpu輪詢到中斷的思路。
就像中斷需要更多的硬件支持一樣,改進版的tail也需要更多操作系統和文件系統到支持。
網上的相關資料
GNU?coreutils里的實現,也是我們在Linux里用到到命令,其實我們經常用到的命令實現起來也是不容易到,tail.c有兩千多行代碼。
JavaEye里INotify的介紹?,主要是inotify-tools這個工具到介紹,使用這個工具可以實現shell腳本監視文件系統的變化
Inotify的介紹?,里面有簡潔的示例代碼,我后面用c實現的copy了這里到代碼。
http://jnotify.sourceforge.net/? jni實現的跨平臺的文件系統監控庫,支持win32/64 Linux 和 Mac os(我只測試了Linux,不過它有其它系統到動態鏈接庫文件)
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
#include
#include
#include
#include
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 ev = (WatchEvent)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;
}
}
}
}
總結
以上是生活随笔為你收集整理的java tail -f 后返回_tail -f 的实现 | 学步园的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: u盘文件过大怎么回事 U盘文件太大如何处
- 下一篇: 宏?台式电脑怎么u盘启动 宏碁台式机如何