新手的 tail 命令实现
本人 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, 是我没找到么, 知道的同学指教一下
可以读特定位置,但是不能指定行