如何通过Node.js中的child_process.spawn()将长字符串传递给/ dev / stdin?

时间:2014-03-26 11:02:35

标签: node.js

我尝试通过stdin传递数据来执行Inkscape。 Inkscape仅通过/dev/stdin支持此功能。基本上,我试图做这样的事情:

echo "<sgv>...</svg>" | inkscape -z -f /dev/stdin -A /dev/stdout

我不想将SVG写入磁盘。

我尝试使用stdin.write(),但它不起作用(可能是因为/dev/stdin):

var cmd = spawn("inkscape", ["-z", "-f", "/dev/stdin", "-A", "/dev/stdout"], {encoding: "buffer", stdio: ["pipe", stdoutPipe, "pipe"]});

cmd.stdin.write(svg);

这确实有效,但我必须将SVG写入磁盘:

var cmd = spawn("inkscape", ["-z", "-f", "/dev/stdin", "-A", "/dev/stdout"], {encoding: "buffer", stdio: [fs.openSync('file.svg', "a"), stdoutPipe, "pipe"]});

我尝试将流传递给stdio,但我只是继续TypeError: Incorrect value for stdio stream: [object Object]

有什么想法吗?

附录

示例使用Inkscape,但我的问题适用于使用/dev/stdin的任意程序。

顺便说一句,这对我有用:

var exec = require('child_process').exec;
exec("echo \"<svg>...</svg>\" | inkscape -z -f /dev/stdin -A /dev/stdout | cat", function (error, stdout, stderr) {});

除了我的SVG太长,所以它会抛出一个错误:Error: spawn Unknown system errno 7

4 个答案:

答案 0 :(得分:9)

好吧,我没有Inkscape,但这似乎解决了Node.js方面的问题。我在Inkscape中使用wc作为支持者; -c选项只输出给定文件中的字节数(在本例中为/dev/stdin)。

var child_process = require('child_process');

/**
 * Create the child process, with output piped to the script's stdout
 */
var wc = child_process.spawn('wc', ['-c', '/dev/stdin']);
wc.stdout.pipe(process.stdout);

/**
 * Write some data to stdin, and then use stream.end() to signal that we're
 * done writing data.
 */
wc.stdin.write('test');
wc.stdin.end();

这个技巧似乎表明你已经完成了对流的写作。根据SVG的大小,您可能需要通过处理'drain'事件从Inkscape pay attention to backpressure开始。


至于将流传递到child_process.spawn调用,您需要使用'pipe'选项,然后将可读流传输到child.stdin,如下所示。我知道这在Node v0.10.26中有效,但在此之前不确定。

var stream = require('stream');
var child_process = require('child_process');

/**
 * Create the child process, with output piped to the script's stdout
 */
var wc = child_process.spawn('wc', ['-c', '/dev/stdin'], {stdin: 'pipe'});
wc.stdout.pipe(process.stdout);

/**
 * Build a readable stream with some data pushed into it.
 */
var readable = new stream.Readable();
readable._read = function noop() {}; // See note below
readable.push('test me!');
readable.push(null);

/**
 * Pipe our readable stream into wc's standard input.
 */
readable.pipe(wc.stdin);

显然,这个方法有点复杂,你应该使用上面的方法,除非你有充分的理由(你有效地实现了你自己的可读字符串)。

注意:readable._push函数必须根据docs实现,但不一定要做任何事情。

答案 1 :(得分:5)

所以,我想出了一个解决方法。这看起来有点像黑客,但它的工作正常。

首先,我制作了这个单行shell脚本:

cat | inkscape -z -f /dev/stdin -A /dev/stdout | cat

然后,我只是生成该文件并写入stdin,如下所示:

cmd = spawn("shell_script");

cmd.stdin.write(svg);
cmd.stdin.end();
cmd.stdout.pipe(pipe);

我真的认为这应该可以在没有shell脚本的情况下工作,但它不会(至少对我来说)。这可能是Node.js的错误。

答案 2 :(得分:5)

问题来自于节点中的文件描述符是套接字并且linux(可能是大多数Unices)如果它是套接字而不能让你打开/ dev / stdin。

我在https://github.com/nodejs/node-v0.x-archive/issues/3530#issuecomment-6561239

上找到了bnoordhuis的解释

给定的解决方案接近@ nmrugg的回答:

var run = spawn("sh", ["-c", "cat | your_command_using_dev_stdin"]);

进一步的工作之后,您现在可以使用https://www.npmjs.com/package/posix-pipe模块来确保进程看到的stdin不是套接字。

看看&#39;应该将数据传递给子进程&#39;在这个模块中进行测试,归结为

var p = pipe()
var proc = spawn('your_command_using_dev_stdin', [ .. '/dev/stdin' .. ],
    { stdio: [ p[0], 'pipe', 'pipe' ] })
p[0].destroy() // important to avoid reading race condition between parent/child
proc.stdout.pipe(destination)
source.pipe(p[1])

答案 3 :(得分:0)

Inkscape bug 171016所示,Inkscape不支持通过stdin导入,但它在愿望清单上。