30

我的问题与此类似:如何检测我的 shell 脚本是否通过管道运行?. 不同之处在于我正在处理的 shell 脚本是用 Node.js 编写的。

假设我输入:

echo "foo bar" | ./test.js

那么我怎样才能获得价值"foo bar"test.js

我读过Unix and Node: Pipes and Streams但这似乎只提供了一个异步解决方案(除非我弄错了)。我正在寻找一个同步的解决方案。此外,使用这种技术,检测脚本是否正在通过管道传输似乎不是很简单。

TL; DR 我的问题有两个:

  1. 如何检测 Node.js 脚本是否通过 shell 管道运行,例如echo "foo bar" | ./test.js
  2. 如果是这样,如何读出 Node.js 中的管道值?
4

5 回答 5

49

我刚刚为我的部分问题找到了一个更简单的答案。

要快速同步地检测管道内容是否正在传递到 Node.js 中的当前脚本,请使用process.stdin.isTTY布尔值:

$ node -p -e 'process.stdin.isTTY'
true
$ echo 'foo' | node -p -e 'process.stdin.isTTY'
undefined

因此,在脚本中,您可以执行以下操作:

if (process.stdin.isTTY) {
  // handle shell arguments
} else {
  // handle piped content (see Jerome’s answer)
}

我之前没有找到这个的原因是因为我正在查看文档process,其中isTTY根本没有提到。相反,它在TTY 文档中有所提及。

于 2013-03-18T19:39:43.980 回答
24

管道用于处理像“foo bar”这样的小输入,但也可以处理大文件。

流 API 确保您可以开始处理数据,而无需等待大文件完全通过管道传输(这对速度和内存更好)。它这样做的方式是为您提供大量数据。

管道没有同步 API。如果你真的想在做某事之前掌握整个管道输入,你可以使用

注意:仅使用node >= 0.10.0因为示例使用 stream2 API

var data = '';
function withPipe(data) {
   console.log('content was piped');
   console.log(data.trim());
}
function withoutPipe() {
   console.log('no content was piped');
}

var self = process.stdin;
self.on('readable', function() {
    var chunk = this.read();
    if (chunk === null) {
        withoutPipe();
    } else {
       data += chunk;
    }
});
self.on('end', function() {
   withPipe(data);
});

测试

echo "foo bar" | node test.js

node test.js
于 2013-03-17T23:25:32.423 回答
9

事实证明这process.stdin.isTTY是不可靠的,因为您可以生成一个不是 TTY 的子进程。

我在这里使用文件描述符找到了更好的解决方案。

您可以通过以下功能测试您的程序是否通过管道输入或输出:

function pipedIn(cb) {
    fs.fstat(0, function(err, stats) {
        if (err) {
            cb(err)
        } else {
            cb(null, stats.isFIFO())
        }
    })
}

function pipedOut(cb) {
    fs.fstat(1, function(err, stats) {
        if (err) {
            cb(err)
        } else {
            cb(null, stats.isFIFO())
        }
    })
}

pipedIn((err, x) => console.log("in", x))
pipedOut((err, x) => console.log("out", x))

这是一些证明它有效的测试。

❯❯❯ node pipes.js
in false
out false
❯❯❯ node pipes.js | cat -
in false
out true
❯❯❯ echo 'hello' | node pipes.js | cat -
in true
out true
❯❯❯ echo 'hello' | node pipes.js
in true
out false
❯❯❯ node -p -e "let x = require('child_process').exec(\"node pipes.js\", (err, res) => console.log(res))"
undefined
in false
out false
❯❯❯ node -p -e "let x = require('child_process').exec(\"echo 'hello' | node pipes.js\", (err, res) => console.log(res))"
undefined
in true
out false
❯❯❯ node -p -e "let x = require('child_process').exec(\"echo 'hello' | node pipes.js | cat -\", (err, res) => console.log(res))"
undefined
in true
out true
❯❯❯ node -p -e "let x = require('child_process').exec(\"node pipes.js | cat -\", (err, res) => console.log(res))"
undefined
in false
out true
于 2019-11-25T01:47:15.593 回答
1

如果您需要在 bash 中使用内联 --eval字符串通过管道进入 nodejs,cat也可以:

$ echo "Hello" | node -e "console.log(process.argv[1]+' pipe');" "$(cat)"
# "Hello pipe"
于 2013-11-29T11:46:31.753 回答
0

您需要像这样检查stdoutstdin不像其他地方建议的那样):

if (process.stdout.isTTY) {
  // not piped
} else {
  // piped
}
于 2017-03-21T15:38:38.587 回答