从标准输入读取所有文本到字符串

Posted

技术标签:

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

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

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

【问题讨论】:

【参考方案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。

【讨论】:

我建议不要像its author doesn't seem to understand what "standard input" really means那样使用这个模块,并且反对修复这个缺陷。 get-stdin-with-tty 是一个似乎可以解决问题的分支。【参考方案5】:

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

    https://github.com/aws/aws-cdk/issues/11314这里报告的问题 https://github.com/nodejs/node/issues/35997 该库的贡献者创建了一个节点问题 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 第一次让我的代码更干净 :) 没有它,如果不存在标准输入数据,这个函数就会挂起——有人可能会说这是一个错误该链接的代码。

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

【讨论】:

以上是关于从标准输入读取所有文本到字符串的主要内容,如果未能解决你的问题,请参考以下文章

C语言从标准输入读取字符,所有非字母字符完全按照输入形式输出,字母字符在输出前加密

从所有MPI流程的标准输入读取

从标准输入读取而不阻塞

Linux Shell学习-sed命令详解

sed 命令编辑文本

IO文件