【问题标题】:Node-cli freeze after Spawning a child_process生成 child_process 后节点 cli 冻结
【发布时间】:2019-08-06 04:21:56
【问题描述】:

cli执行后无法接收键盘输入,这也包括'ctrl-c'和'ctrl-z',所以你必须手动退出程序。给我添了不少麻烦,请看一下;

var { exec, spawn } = require("child_process");
let cmd = (cmdline, consolelog = true) => {
  return new Promise((resolve, reject) => {
    let cmdarray = cmdline.split(" ");
    let result = "";
    let error = "";
    let child = spawn(cmdarray.shift(), cmdarray);
    process.stdin.pipe(child.stdin);
    child.stdout.setEncoding("utf8");
    child.stderr.setEncoding("utf8");
    child.stderr.on("data", data => {
      if (consolelog) process.stdout.write(data.toString());
      error = data.toString();
    });
    child.stdout.on("data", data => {
      if (consolelog) process.stdout.write(data.toString());
      result = data.toString();
    });
    child.on("close", code => {
      if (consolelog) process.stdout.write(`Exit code: ${code}\n`);
      code == 0 ? resolve(result) : reject(error);
    });
  });
};

操作系统:osx & ubuntu 19.04

测试用例:

cmd("echo hi");

编辑:

正常情况:将代码放入myprogram.js,使用node myprogram.js激活脚本。它完美运行,您还可以尝试不同的命令。 但是,如果您使用

输入以下代码
$ node
> let cmd = require(PATH_TO_CMD_FUNCTION)
> cmd("echo hi");

node-cli 将冻结并停止收听您的键盘输入。

编辑 2:

发现了,你需要通过{stdio: "inherit"}进行频道

【问题讨论】:

    标签: node.js spawn


    【解决方案1】:

    更新答案:

    为了简洁起见,我将您的刷怪箱稍微缩小了一点,并消除了任何其他可能性。我可以找到一个常见的测试用例来重现有关信号、键盘快捷键和捕获输入的问题。

    如果您生成“sh”命令,您将无法通过传统的信号键盘快捷键从生成的进程中逃脱。这是因为 node.js “捕获”输入并将其直接转发到生成的进程。

    大多数进程允许通过键盘快捷键(例如 CTRL-C)发出信号来杀死进程。然而,'sh' 没有——因此是一个完美的例子。

    退出的唯一方法是使用“退出”命令、关闭窗口(这可能会使生成的进程在后台运行)、重新启动计算机等。此外,在内部或通过其他方式发送信号,但不是通过标准输入或等效的。

    换句话说,您的 CTRL-C 输入“正常工作”不是因为它正在杀死您的节点应用程序而是因为它正在被转发到生成的进程 并杀死它。

    如果生成的进程免疫,它将继续捕获您的输入。

    require("child_process").spawn("sh", {
      shell:     true,
      encoding:  'utf8',
      stdio:     [0,1,2]
    });
    

    这可能不是您特定程序的最佳示例,但它说明了原理,这是我能得出的最接近的原理,因为我无法使用给定的测试用例进行复制(我已经在手机、笔记本电脑和我的云服务器,三个不同版本的节点,两个不同版本的 Ubuntu)。

    无论如何,听起来您的标准输入并没有被生成的进程“放开”。您可能需要将其“重新分配”给原始 process.stdin 。

    如此处所述:

    https://node.readthedocs.io/en/latest/api/child_process/

    另外,请注意节点为 'SIGINT' 和 'SIGTERM',因此它不会因收到这些信号而终止, 它会退出。

    以前:

    由于拆分和移位,您的 cmd 函数似乎只获取一个参数(命令本身)。 Spawn 需要一个带有整个命令的字符串,因此很可能它只会得到没有“hi”的“echo”,所以它不会因为挂在“echo”上而退出。可能还需要附加换行符(“\n”)。

    将命令嵌套在 sh 命令中然后执行它也可能会有所帮助,因此它在 shell 中运行。

    像这样:

    var { exec, spawn } = require("child_process");
    let cmd = (cmdline, consolelog = true) => {
      return new Promise((resolve, reject) => {
        let result = "";
        let error = "";
    
        // This.  Note the shell option.
        let child = spawn(cmdline, {shell:true});
    
        process.stdin.pipe(child.stdin);
        child.stdout.setEncoding("utf8");
        child.stderr.setEncoding("utf8");
        child.stderr.on("data", data => {
          if (consolelog) process.stdout.write(data.toString());
          error = data.toString();
        });
        child.stdout.on("data", data => {
          if (consolelog) process.stdout.write(data.toString());
          result = data.toString();
        });
        child.on("close", code => {
          if (consolelog) process.stdout.write(`Exit code: ${code}\n`);
          code == 0 ? resolve(result) : reject(error);
        });
      });
    };
    
    cmd("echo hello");
    

    输出:

    hello
    Exit code: 0
    

    【讨论】:

    • 很可能你没有运行代码?正常情况下可以正常使用
    • 当您说“正常”与不按预期工作时,有什么不同?您只提供了一个测试用例。
    • 如果 cli 在附加了 spawn 后没有 stdin/stdout,那是因为它仍然附加。
    • {shell:true}虽然没用,我曾经也认为process.stdin&process.stdout有问题,我简单地在执行前存储了两个状态,执行后替换它们,什么也没发生,结果还是一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-09
    • 2021-07-18
    • 1970-01-01
    • 2022-08-06
    • 2019-07-11
    • 2013-12-29
    • 1970-01-01
    相关资源
    最近更新 更多