如何通过Node.js中的child_process.spawn()将长string传递给/ dev / stdin?
我试图通过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"]});
我试图传递一个stream到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
好吧,我没有Inkscape,但这似乎解决了Node.js方面的事情。 我使用wc
作为Inkscape的一个立场; -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();
这个技巧似乎表明你已经完成了写入stream。 根据你的SVG的大小,你可能需要注意 Inkscape的背压处理'drain'
事件。
至于传递一个stream到child_process.spawn
调用中,您需要使用'pipe'
选项,然后将可读的stream传child.stdin
,如下所示。 我知道这在节点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);
显然,这个方法有点复杂,除非你有足够的理由(你正在有效地实现你自己的可读string),否则你应该使用上面的方法。
注意: readable._push
函数必须根据文档来实现,但不一定要做任何事情。
所以,我想出了一个解决方法。 这似乎有点破解,但它工作得很好。
首先,我做了这一行的shell脚本:
cat | inkscape -z -f /dev/stdin -A /dev/stdout | cat
然后,我只是产生该文件,并写入标准input,如下所示:
cmd = spawn("shell_script"); cmd.stdin.write(svg); cmd.stdin.end(); cmd.stdout.pipe(pipe);
我真的认为这应该没有shell脚本,但它不会(至less对我来说)。 这可能是一个Node.js错误。
问题来自于节点中的文件描述符是套接字,并且如果是套接字,linux(也可能是大多数Unices)不会让你打开/ dev / stdin。
给出的解决scheme接近@ nmrugg的答案:
var run = spawn("sh", ["-c", "cat | your_command_using_dev_stdin"]);
进一步的工作后,你现在可以使用https://www.npmjs.com/package/posix-pipe模块来确保进程看到一个不是套接字的stdin。
看看这个模块中“应该把数据传给subprocess”的testing归结为什么
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])
正如Inkscape错误171016所示,Inkscape不支持通过stdin导入,但是它已经在他们的Wishlist上。