【问题标题】:How do you pipe a long string to /dev/stdin via child_process.spawn() in Node.js?如何通过 Node.js 中的 child_process.spawn() 将长字符串传送到 /dev/stdin?
【发布时间】:2014-03-26 11:02:34
【问题描述】:

我正在尝试通过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

【问题讨论】:

    标签: node.js


    【解决方案1】:

    好吧,我没有 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来实现,但不一定非要做什么。

    【讨论】:

    • 感谢您的建议。但是当我运行这些脚本中的任何一个时,我都会收到“错误:写 EPIPE”,但如果我从数组中删除 '/dev/stdin/',它们就会起作用。我正在运行 v0.10.26。我做错了吗?
    • 你在什么操作系统上运行?我刚刚仔细检查了我的系统(运行 OS X 10.9.2 的 Macbook Pro),两个脚本都运行良好。你有wc 安装吗?
    • 有趣。我正在使用 Ubuntu。我确实安装了wc。如果我删除 /dev/stdin,这些命令可以正常工作。也许我会将此报告为错误。
    • 非常有趣,我要去挖掘一下,如果我发现了什么我会跟进。
    【解决方案2】:

    所以,我想出了一个解决方法。这看起来有点像 hack,但效果很好。

    首先,我制作了这个单行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 错误。

    【讨论】:

      【解决方案3】:

      问题在于节点中的文件描述符是套接字,如果 /dev/stdin 是套接字,linux(可能还有大多数 Unices)不会让你打开它。

      我在 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 模块来确保进程看到的标准输入不是套接字。

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

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

      【讨论】:

        【解决方案4】:

        正如Inkscape bug 171016 所指出的,Inkscape 不支持通过标准输入导入,但它在他们的愿望清单上。

        【讨论】:

        • 不直接支持,但是可以使用/dev/stdin。第二个示例工作正常,并且执行“cat file.svg | inkscape -z -f /dev/stdin -A /dev/stdout”之类的操作也可以。我只是不知道如何让节点输入数据。
        猜你喜欢
        • 1970-01-01
        • 2017-08-21
        • 1970-01-01
        • 2013-02-26
        • 2020-11-30
        • 1970-01-01
        • 1970-01-01
        • 2019-05-14
        • 1970-01-01
        相关资源
        最近更新 更多