新手的 tail 命令实现
发布于 5 年前 作者 Luyakus 3202 次浏览 来自 分享

本人 iOSer, 最近不是太忙, 下决心扩展自己的能力范围, 于是乎开始学 node, 感觉极大的扩展了自己编程能力的应用范围, 学了 fs, stream 模块之后, 写了一个简易的 tail 命令

node test-ntail.js ../colors.js 13 // colors.js 是被观察的文件, test-ntail.js 是测试文件, 支持行数, 正数从文件头开始数, 负数从文件尾部开始数

ntail.js


const { Readable } = require('stream')
const fs = require('fs')

class NTail extends Readable {
    constructor(path, opts) {
        super(path, opts)
        const{ watchLineNum } = opts
        let watchLineNumInt = parseInt(watchLineNum, 10)
        if (fs.existsSync(path)) {
            this.fd = fs.openSync(path)
            this.position = this.gotoWatchLineNum(watchLineNumInt)
            process.on('SIGINT', () => {
                fs.closeSync(this.fd)
                process.exit(1)
            })
        } else {
            throw Error(`${path} 文件不存在`)
        }
    }

    _read(size) {
       this.pushTailData()
    }

    pushTailData() {
        let buffer = Buffer.alloc(4 * 1024)
        fs.read(this.fd, buffer, 0, buffer.length, this.position, (err, readSize, buffer) => {
            this.push(buffer.slice(0, readSize))
            this.position += readSize
        })
    }
	
    gotoWatchLineNum (watchLineNum) {
        let fileSize = fs.fstatSync(this.fd).size
        let char = Buffer.alloc(1)
        let lineNum = 0
        let readSize = 0
        do {
            if(watchLineNum  >= 0) {
                fs.readSync(this.fd, char, 0, 1, null)
                if (char.toString() === '\n') {
                    lineNum += 1
                }
            } else {
                fs.readSync(this.fd, char, 0, 1, fileSize - readSize)
                if (char.toString() === '\n') {
                    lineNum -= 1
                }
            }
            readSize += 1
        } while(lineNum !== watchLineNum)
        if (watchLineNum >= 0) {
            return readSize
        } else {
            return fileSize - (readSize - 2)
        }
    }
}

module.exports = NTail

test-ntail.js

const NTail = require('./ntail')

let file = process.argv[2]
let lineNum = process.argv[3]
let tail = new NTail(file, {watchLineNum: lineNum})
tail.pipe(process.stdout)

iOS 也算是前端的一种, 学习了 node 之后, 感觉有了一种轻松的方式跟系统本身打交道, 原来是框架的使用者, node 为我们提供了生产资料, 我们现在也可以变成框架的创造者, 非常开心, 祝 node 长青

2 回复

写的过程中发现 node 没有提供到文件特定位置的 api, 是我没找到么, 知道的同学指教一下

可以读特定位置,但是不能指定行

回到顶部