'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 <文件名>');
}

silenceboy
1.1k 声望46 粉丝

coding for fun