2014-03-26 25 views
15

我想通过stdin传递数据来执行Inkscape。 Inkscape仅通过/dev/stdin支持此功能。基本上,我试图做这样的事情:如何通过Node.js中的child_process.spawn()将长字符串传递给/ 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

回答

3

所以,我想出了一个解决办法。这似乎有点破解,但它工作得很好。

首先,我做这一行的shell脚本:

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

然后,我只是因为它产生的文件,并写入标准输入这样的:

cmd = spawn("shell_script"); 

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

我真的觉得这应该不工作shell脚本,但它不会(至少对我而言)。这可能是一个Node.js错误。

1

由于Inkscape bug 171016表示,Inkscape中不支持通过进口stdin,但它在他们的愿望清单中。

+1

它不直接支持它,但你可以使用/ dev /标准输入。第二个例子工作正常,并且执行类似“cat file.svg | inkscape -z -f/dev/stdin -A/dev/stdout”的作品。我只是无法弄清楚如何让数据中的节点管道。 – nmrugg

9

好吧,我没有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(); 

该技巧似乎表明您已完成写入流的操作。根据您的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执行,但它不一定要做任何事情。

+0

感谢您的建议。但是当我运行这些脚本时,我会得到“错误:写入EPIPE”,但是如果我从数组中删除“/ dev/stdin /”,它们就会工作。我正在运行v0.10.26。难道我做错了什么? – nmrugg

+0

你在运行什么操作系统?我只是检查了我的系统(Macbook Pro运行OS X 10.9.2)并且两个脚本运行良好。你是否安装了'wc'? –

+0

有趣。我正在使用Ubuntu。我确实安装了'wc'。如果我删除'/ dev/stdin',这些命令可以正常工作。也许我会报告这个错误。 – nmrugg

3

问题出在这样的事实,即节点中的文件描述符是套接字,并且如果它是套接字,linux(可能大多数Unices)不会让您打开/ dev/stdin。

我发现bnoordhuis这种解释对https://github.com/nodejs/node-v0.x-archive/issues/3530#issuecomment-6561239

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

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

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

看看这个模块中的“应该将数据传递给子进程的测试,归结为

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]) 
+0

不错。感谢您的解释。 – nmrugg

+0

@nmrugg编辑一个潜在的解决方案,避免“猫|”并可以简化一些代码。没有在真实世界的代码中测试它。 –

相关问题