nodejs实现tail -f功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
'use strict';

const fs = require('fs');

/**
* tailf
*
* @param {String} filename 文件名
* @param {Number} delay 读取不到内容时等待的时间,ms
* @param {Function} onError 操作出错时的回调函数,onError(err)
* @param {Function} onData 读取到文件内容时的回调函数,onData(data)
*/
function tailf(filename, delay, onError, onData) {

// 每次读取文件块大小,16K
const CHUNK_SIZE = 16 * 1024;
// 打开文件,获取文件句柄
fs.open(filename, 'r', (err, fd) => {
if (err) return onError(err);

// 文件开始位置
fs.fstat(fd, (err, stats) => {
if (err) return onError(err);

// 文件开始位置
let position = stats.size;
// 循环读取
const loop = () => {

const buf = Buffer.alloc(CHUNK_SIZE);
fs.read(fd, buf, 0, CHUNK_SIZE, position,
(err, bytesRead, buf) => {
if (err) return onError(err);

// 实际读取的内容长度以 bytesRead 为准
// 并且更新 position 位置
position += bytesRead;
onData(buf.slice(0, bytesRead));

if (bytesRead < CHUNK_SIZE) {
// 如果当前已到达文件末尾,则先等待一段时间再继续
// setTimeout(loop, delay);
} else {
loop();
}
});
};
loop();
// 监听文件变化,如果收到 change 事件则尝试读取文件内容
fs.watch(filename, (event, filename) => {
if (event === 'change') {
loop();
}
});
});
});
}

const filename = process.argv[2];
if (filename) {
tailf(filename, 100, err => {
if (err) console.error(err);
}, data => {
process.stdout.write(data);
});
} else {
console.log('使用方法: node tailf <文件名>');
}