【问题标题】:Stream stdout from subprocess从子进程流标准输出
【发布时间】:2021-08-13 03:59:58
【问题描述】:

我在 Deno 中将 Docker 构建作为子进程运行,并希望将标准输出流式传输到父标准输出 (Deno.stdout),以便立即输出。

我怎样才能做到这一点?

目前我有以下内容,但在子进程完成之前它不会输出任何内容。

const p = Deno.run({
  cmd: ['docker', 'build', '.'],
  stdout: 'piped'
});

const stdout = await p.output();
await Deno.stdout.write(stdout);

【问题讨论】:

    标签: deno


    【解决方案1】:

    离你不远了;您只需要在等待该过程之前启动输出管道的过程。您还可以进行一些其他优化,例如使用 Deno.copy 将子进程的输出通过管道传输到主进程的标准输出,而无需在内存中复制内容。

    import { copy } from "https://deno.land/std@0.104.0/io/util.ts";
    
    const cat = Deno.run({
      cmd: ["docker", "build", "--no-cache", ".", "-t", "foobar:latest"],
      cwd: "/path/to/your/project",
      stdout: "piped",
      stderr: "piped",
    });
    
    copy(cat.stdout, Deno.stdout);
    copy(cat.stderr, Deno.stderr);
    
    await cat.status();
    console.log("Done!");
    

    如果你想在每一行前面加上它来自的进程的名称(当你有多个子进程运行时很有用,你可以创建一个简单的函数,使用 std 库的 readLines 函数和一个文本编码器来做到这一点

    import { readLines } from "https://deno.land/std@0.104.0/io/mod.ts";
    import { writeAll } from "https://deno.land/std@0.104.0/io/util.ts";
    
    async function pipeThrough(
      prefix: string,
      reader: Deno.Reader,
      writer: Deno.Writer,
    ) {
      const encoder = new TextEncoder();
      for await (const line of readLines(reader)) {
        await writeAll(writer, encoder.encode(`[${prefix}] ${line}\n`));
      }
    }
    
    const cat = Deno.run({
      cmd: ["docker", "build", "--no-cache", ".", "-t", "foobar:latest"],
      cwd: "/path/to/your/project",
      stdout: "piped",
      stderr: "piped",
    });
    
    pipeThrough("docker", cat.stdout, Deno.stdout);
    pipeThrough("docker", cat.stderr, Deno.stderr);
    await cat.status();
    console.log("Done!");
    

    【讨论】:

    【解决方案2】:

    p 代表进程,Deno.run returns the process state 在返回时(不是标准输出):

    console.log(await p.status());
    // { success: true, code: 0 }
    

    由于您正在等待状态,stdout 将在进程完成之前不会流式传输输出。

    像这样尝试using the process

    const p = Deno.run({ cmd, stderr: 'piped', stdout: 'piped' });
    const [status, stdout, stderr] = await Promise.all([
      p.status(),
      p.output(),
      p.stderrOutput()
    ]);
    
    console.log(new TextDecoder().decode(stdout)); // since it's returned as UInt8Array
    
    p.close();
    

    但它仍然会等到子进程完成。

    【讨论】:

    • 这有什么不同?在所有承诺都解决之前(在子流程完成之后),我仍然不会得到标准输出。
    • 啊,我明白了。您可能需要从子流程中提供一个流,并从主流程中读取它——但我还没有看到任何关于如何做到这一点的文档