从标准输入读取所有文本到一个字符串中。

78
我正在使用Node.js编写一个程序,在某些情况下,它需要作为简单的过滤器:读取stdin中的所有内容(直到文件末尾),进行一些处理,然后将结果写入stdout。
如何实现“读取stdin中的所有内容”部分?目前我找到的最接近的解决方案似乎只适用于从控制台逐行读取或仅在stdin是文件而不是管道时才起作用。
5个回答

95
我的模板对于这个问题的解决方案与上面的评论中描述的解决方案非常相似 - 将其提供在顶层,因为这是最简单的方法,而且不应该只是在评论中提到。
var fs = require('fs');
var data = fs.readFileSync(0, 'utf-8');
// data is a string containing the file's contents

48
使用fs.readFileSync(process.stdin.fd, 'utf-8')可能更易读,这样读者就清楚地知道它是标准输入。 - Nicholas Daley-Okoye
7
@NicholasDaley,当我在Node v11.11.0上使用你的技术时,出现了以下错误:Error: EAGAIN:resource temporarily unavailable, read。但是这个答案中的方法仍然可以正常工作。 - Sam
3
如果你将一个巨大的文件通过stdin进行管道传输,那么这个操作将会失败。 - user949300
6
当我使用这个方法时,在Windows上出现了一个错误:TypeError: Cannot read property '1' of undefined。 - szx
2
我花了数小时来解决这个问题:如果你将stdin导入到程序中,它在Windows上无法工作。如果你想知道原因,可以从这里开始深入研究:https://github.com/aws/aws-cdk/issues/11314 - Daniel Kaplan
显示剩余5条评论

44

我在使用Node 11+中的以下内容

 async function read(stream) {
   const chunks = [];
   for await (const chunk of stream) chunks.push(chunk); 
   return Buffer.concat(chunks).toString('utf8');
 }

用法:

const input = await read(process.stdin);

4
处理具有非常长输入的作品 - Cobertos
这不是同步的。 - Daniel Kaplan
将块作为缓冲区读取,然后像这样连接它们似乎比立即将它们转换为字符串更有效:在块点之间我得到了奇怪的伪影。 - Gamma032
如果交互式输入没有以换行符结尾,我必须按两次Ctrl + D才能使用输入,我想知道是否有解决方法。然而,这是我能找到的唯一适用于管道和交互式的方法。遗憾的是它不是同步的,相关链接:https://dev59.com/JnA75IYBdhLWcg3wK14p 在v16.14.2,Ubuntu 22.10上测试通过。 - Ciro Santilli OurBigBook.com

34

如果您使用的是 Linux 系统,则无需使用第三方软件包进行此操作。当然,需要考虑您的性能需求,但以下两行代码可以解决问题:

const fs = require("fs");
const data = fs.readFileSync("/dev/stdin", "utf-8");

在下面的评论中,Jan指出更具可移植性的解决方案是使用0,因为这是POSIX标准。因此,您可以简单地使用:

Jan在下面的评论中指出,使用0是更具可移植性的解决方案,因为它符合POSIX标准。因此,您只需简单地使用:

const fs = require("fs");
const data = fs.readFileSync(0, "utf-8");

data 现在是一个从标准输入读取的数据字符串,解释为 utf-8 编码。


17
更好的方法是使用fs.readFileSync(0, 'utf8'),这在任何地方都可以工作。(文件描述符0是标准输入)。 - Jan Schär
3
@Jan Schär,在Windows上无法工作(TypeError: Cannot read property '1' of undefined)。但是这个可以。 - ikegami
1
'/dev/stdin'0 在 macOS 上都可以使用。 - knpwrs

13

get-stdin可以解决问题。


在你的问题中阅读到一些隐含信息。

由于你标记了问题为“同步”,我需要指出stdin在node.js中仅支持异步。上面提到的库是最简单的方法。它将整个输入处理为字符串或缓冲区。

如果可能,以流式编程方式编写程序是最好的,但某些用例对于流式编程是可行的(例如单词计数),而某些用例则不然(例如反转输入)。

另外,“从控制台逐行读取”是终端缓冲区存储您的按键所导致的现象。如果想要更多细节,请查看神奇的TTY Demystified


12
我建议不要使用这个模块,因为它的作者似乎不理解"标准输入"的真正含义,并且反对修复此缺陷。 - Vladimir Panteleev
4
“get-stdin-with-tty”是一个分支,似乎解决了这个问题。 - Adam C

3

我在这里没有看到实际上同步的解决方案,除了@Patrick Narkinsky的。但是@Patrick Narkinsky的答案在Windows上不起作用。似乎是一个node.js的bug。如果你想了解详情,请随意深入研究这些github问题,但是我在阅读了一个小时后放弃了。

  1. https://github.com/aws/aws-cdk/issues/11314 这里报告的问题
  2. https://github.com/nodejs/node/issues/35997 该库的贡献者创建了一个node问题
  3. https://github.com/libuv/libuv/pull/3053 一个nodejs贡献者提交了一个修复(?)的PR(尚未合并)

我在那里没有找到解决方法(可能是我忽略了),但我意外地发现了一个解决问题的方法。它不太美观,但它有效。由于该链接只显示如何记录进度,因此我必须根据自己的需求进行修改:

import fs from 'fs';
const BUFSIZE = 256;
const buf = Buffer.alloc(BUFSIZE);
let bytesRead;
let stdin = '';

export function stdinToString(): string {
  do {
    // Loop as long as stdin input is available.
    bytesRead = 0;
    try {
      bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, null);
    } catch (e) {
      if (e.code === 'EAGAIN') {
        // 'resource temporarily unavailable'
        // Happens on OS X 10.8.3 (not Windows 7!), if there's no
        // stdin input - typically when invoking a script without any
        // input (for interactive stdin input).
        // If you were to just continue, you'd create a tight loop.
        throw 'ERROR: interactive stdin input not supported.';
      } else if (e.code === 'EOF') {
        // Happens on Windows 7, but not OS X 10.8.3:
        // simply signals the end of *piped* stdin input.
        break;
      }
      throw e; // unexpected exception
    }
    if (bytesRead === 0) {
      // No more stdin input available.
      // OS X 10.8.3: regardless of input method, this is how the end
      //   of input is signaled.
      // Windows 7: this is how the end of input is signaled for
      //   *interactive* stdin input.
      break;
    }
    // Process the chunk read.
    stdin += buf.toString(undefined, 0, bytesRead);
  } while (bytesRead > 0);

  return stdin;
}

我已经编程十多年了,这是第一次使用“do while”使我的代码更简洁 :) 如果没有它,这个函数将会挂起,如果没有stdin数据存在的话 - 可以说这是那个链接中代码的一个错误。这回答了原始的问题,并且适用于所有操作系统。

我仍在使用这个解决方案,但我已经停止在一个地方使用它。我不记得确切的原因,但我认为如果没有传入标准输入,它会挂起。上面的代码可能有一个简单的修复方法来处理这个问题,但我还没有研究过。 - Daniel Kaplan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接