我希望处理一个文本文件与节点使用命令行调用,如:

节点app.js < input.txt

文件的每一行都需要单独处理,但是一旦处理了输入行就可以忘记。

使用stdin的on-data侦听器,我得到输入蒸汽按字节大小分块,所以我设置了这个。

process.stdin.resume();
process.stdin.setEncoding('utf8');

var lingeringLine = "";

process.stdin.on('data', function(chunk) {
    lines = chunk.split("\n");

    lines[0] = lingeringLine + lines[0];
    lingeringLine = lines.pop();

    lines.forEach(processLine);
});

process.stdin.on('end', function() {
    processLine(lingeringLine);
});

但这看起来太草率了。必须围绕行数组的第一个和最后一个项目进行按摩。就没有更优雅的方式了吗?


当前回答

#!/usr/bin/env node

const EventEmitter = require('events');

function stdinLineByLine() {
  const stdin = new EventEmitter();
  let buff = '';

  process.stdin
    .on('data', data => {
      buff += data;
      lines = buff.split(/\r\n|\n/);
      buff = lines.pop();
      lines.forEach(line => stdin.emit('line', line));
    })
    .on('end', () => {
      if (buff.length > 0) stdin.emit('line', buff);
    });

  return stdin;
}

const stdin = stdinLineByLine();
stdin.on('line', console.log);

其他回答

#!/usr/bin/env node

const EventEmitter = require('events');

function stdinLineByLine() {
  const stdin = new EventEmitter();
  let buff = '';

  process.stdin
    .on('data', data => {
      buff += data;
      lines = buff.split(/\r\n|\n/);
      buff = lines.pop();
      lines.forEach(line => stdin.emit('line', line));
    })
    .on('end', () => {
      if (buff.length > 0) stdin.emit('line', buff);
    });

  return stdin;
}

const stdin = stdinLineByLine();
stdin.on('line', console.log);

readline是专门设计用于使用terminal(即process.stdin.isTTY === true)。有很多模块为通用流提供拆分功能,比如split。它让事情变得超级简单:

process.stdin.pipe(require('split')()).on('data', processLine)

function processLine (line) {
  console.log(line + '!')
}

你可以使用readline模块逐行从stdin中读取:

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', (line) => {
    console.log(line);
});

rl.once('close', () => {
     // end of input
 });

如果你想先问用户行数:

//array to save line by line let xInputs = []; const getInput = async (resolve)=>{ const readline = require('readline').createInterface({ input: process.stdin, output: process.stdout, }); readline.on('line',(line)=>{ readline.close(); xInputs.push(line); resolve(line); }) } const getMultiInput = (numberOfInputLines,callback)=>{ let i = 0; let p = Promise.resolve(); for (; i < numberOfInputLines; i++) { p = p.then(_ => new Promise(resolve => getInput(resolve))); } p.then(()=>{ callback(); }); } //get number of lines const readline = require('readline').createInterface({ input: process.stdin, output: process.stdout, terminal: false }); readline.on('line',(line)=>{ getMultiInput(line,()=>{ //get here the inputs from xinputs array }); readline.close(); })

在我的例子中,程序(elinks)返回的行看起来是空的,但实际上有特殊的终端字符、颜色控制代码和退格,所以在其他答案中提供的grep选项对我不起作用。所以我用Node.js写了这个小脚本。我说这个文件很紧,但那只是个随机的名字。

#!/usr/bin/env node

function visible(a) {
    var R  =  ''
    for (var i = 0; i < a.length; i++) {
        if (a[i] == '\b') {  R -= 1; continue; }  
        if (a[i] == '\u001b') {
            while (a[i] != 'm' && i < a.length) i++
            if (a[i] == undefined) break
        }
        else R += a[i]
    }
    return  R
}

function empty(a) {
    a = visible(a)
    for (var i = 0; i < a.length; i++) {
        if (a[i] != ' ') return false
    }
    return  true
}

var readline = require('readline')
var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false })

rl.on('line', function(line) {
    if (!empty(line)) console.log(line) 
})