【问题标题】:Read all text from stdin to a string从标准输入读取所有文本到字符串
【发布时间】:2015-08-07 02:13:55
【问题描述】:

我正在用 Node.js 编写一个程序,它(在某些情况下)想要充当一个简单的过滤器:从标准输入读取所有内容(直到文件末尾),进行一些处理,将结果写入标准输出。

您如何执行“从标准输入读取所有内容”部分?到目前为止,我发现的最接近的解决方案似乎可以从控制台一次处理一行,或者仅在 stdin 是文件而不是管道时才有效。

【问题讨论】:

    标签: node.js stdin synchronous


    【解决方案1】:

    我的样板文件很像上面评论中描述的解决方案 - 在顶层提供它,因为它是执行此操作的最简单的方法,它不应该只在评论中。

    var fs = require('fs');
    var data = fs.readFileSync(0, 'utf-8');
    // Data now points to a buffer containing the file's contents
    

    【讨论】:

    • fs.readFileSync(process.stdin.fd, 'utf-8'); 可能更具可读性,这样阅读它的人就会清楚它是标准输入。
    • @NicholasDaley,在 Node v11.11.0 上使用您的技术时出现以下错误:Error: EAGAIN: resource temporarily unavailable, read。不过,这个答案中的那个仍然可以。
    • @Sam 是的,我在不使用管道时复制,只是终端。我想知道这怎么可能,因为process.stdin.fd === 0 似乎总是成立。如果之前仅访问过 fs.stdin.fd0 也会出现问题,例如:const fs = require('fs'); console.log(process.stdin.fd); console.log(fs.readFileSync(0, 'utf8'));
    • 如果你将一个巨大的文件传送到标准输入,这将失败
    • 在 Windows 上使用此方法时出现错误:TypeError: Cannot read property '1' of undefined
    【解决方案2】:

    如果您使用的是 linux,则无需为此使用 3rd 方包。当然,请考虑您的性能需求,但这两行将起作用:

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

    Jan 在下面的 cmets 中指出,更便携的解决方案是使用 0,因为这是 POSIX 标准。所以,你可以简单地使用:

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

    data 现在是一个字符串,其中包含来自标准输入的数据,解释为 utf 8

    【讨论】:

    • 更好:使用fs.readFileSync(0, 'utf8'),它应该可以在任何地方使用。 (文件描述符 0 是标准输入)。
    • @Jan Schär,不适用于 Windows。 (TypeError: Cannot read property '1' of undefined)。但是this 可以。
    【解决方案3】:

    我在 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】:

    get-stdin 可以解决问题。


    在您问题的字里行间读到一些注释。

    由于您将问题标记为“同步”,因此我只需要注意 stdin 在 node.js 中是异步的。上面的库是它得到的最简单的。它会将整个输入作为字符串或缓冲区处理。

    如果可能,最好以流式风格编写程序,但有些用例适用于流式传输(即字数统计),有些则不可行(即反转输入)。

    此外,“从控制台一次一行”是终端缓冲击键的产物。如果您想要一些“对不起,我问了”级别的详细信息,请查看惊人的 the TTY Demystified

    【讨论】:

    【解决方案5】:

    除了@Patrick Narkinsky 的解决方案之外,我还没有在这里看到实际上是同步的解决方案。但是 @Patrick Narkinsky 的答案在 Windows 上不起作用。 似乎是一个 node.js 错误。如果您想了解详细信息,请随意进入github问题的这个兔子洞,但我在阅读了一个小时后放弃了。

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

    我在那里找不到解决方法(我可能掩盖了它),但是I accidentally stumbled on a solution to the problem. It's not pretty, but it works。由于该链接仅显示如何记录进度,因此我不得不根据自己的需要对其进行修改:

    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 第一次让我的代码更干净 :) 没有它,如果不存在标准输入数据,这个函数就会挂起——有人可能会说这是一个错误该链接的代码。

    这回答了原始问题并且适用于所有操作系统。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-10
      • 2013-09-12
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多