从标准输入读取所有文本到字符串
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.fd
,0
也会出现问题,例如: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
第一次让我的代码更干净 :) 没有它,如果不存在标准输入数据,这个函数就会挂起——有人可能会说这是一个错误该链接的代码。
这回答了原始问题并且适用于所有操作系统。
【讨论】:
以上是关于从标准输入读取所有文本到字符串的主要内容,如果未能解决你的问题,请参考以下文章