当子进程提示输入时,child_process.spawn()挂起

时间:2019-04-17 08:56:19

标签: node.js child-process

我编写了一个节点脚本来管理git存储库到AWS自动伸缩组的部署。

该脚本使用child_process.spawn()来自动执行git,克隆存储库,签出标签等。

如果git可以找到适当的凭据,则效果很好。但是,如果未自动找到凭据,则生成的进程将尝试提示输入凭据,此时将挂起。甚至Ctrl-C也无法退出。整个外壳会话必须结束。

spawn()调用包装在一个函数中以返回Promise。我的功能看起来像这样...

const cp = require('child_process');

let spawn_promise = (command, args, options, stream_output) => {
    return new Promise((resolve, reject) => {
        console.log(chalk.cyan(`${command} [${args}]`));

        let childProcess = cp.spawn(command, args, options);
        let std_out = '';
        let std_err = '';

        childProcess.stdout.on('data', function (data) {
            std_out += data.toString();
            if (stream_output)
                console.log(chalk.green(data.toString()));
        });
        childProcess.stderr.on('data', function (data) {
            std_err += data.toString();
            if (stream_output)
                console.log(chalk.red(data.toString()));
        });

        childProcess.on('close', (code) => {
            if (code === 0) {
                console.log(chalk.blue(`exit_code = ${code}`));
                return resolve(std_out);
            }
            else {
                console.log(chalk.yellow(`exit_code = ${code}`));
                return reject(std_err);
            }
        });

        childProcess.on('error', (error) => {
            std_err += error.toString();
            if (stream_output)
                console.log(chalk.red(error.toString()));
        });
    });
}

我这样称呼...

return spawn_promise('git', ['fetch', '--all'], {env: process.env})
   .then(() => {
      ...

大多数情况下效果很好,并且可以轻松操纵输出和错误等。

但是,如果生成的进程需要输入,我很难找到一种处理输入的好方法。

该问题的一个临时解决方法是添加一个环境变量以防止git提示输入凭据,如果在用户环境中找不到凭据,则抛出错误。但是,这不是理想的。理想情况下,我希望能够优雅地处理标准输入,并且仍然能够像我目前所做的那样捕获并处理输出和错误。

我可以通过执行此操作来解决输入问题...

let childProcess = cp.spawn(command, args, { stdio: [process.stdin, process.stdout, process.stderr] });

这允许git正确提示输入凭据。但是我随后失去了捕获命令输出的功能。

能够解决此问题的正确方法是什么?

我还应该提到,该函数还可以自动化一些相对较长的进程,以构建AMI等。这就是“ stream_output”参数的作用。我希望能够实时查看命令的输出,而不是等到过程完成为止。

1 个答案:

答案 0 :(得分:3)

child_process具有stdin来处理输入,并且在child_process运行时,可以使用它们来输入输入。

请参见下面的示例:

test.sh

#!/bin/sh

echo "Please enter something:"
read ch
echo "Thanks"

当我在此终端上运行时:

shell-input $ ./test.sh 
Please enter something:
something
Thanks
shell-input $ 

当我使用您的代码运行此代码时: test.js

const cp = require('child_process');
const chalk = require('chalk');

let spawn_promise = (command, args, options, stream_output) => {
    return new Promise((resolve, reject) => {
        console.log(chalk.cyan(`${command} [${args}]`));

        let childProcess = cp.spawn(command, args, options);
        let std_out = '';
        let std_err = '';

        childProcess.stdout.on('data', function (data) {
            std_out += data.toString();
            if (stream_output)
                console.log(chalk.green(data.toString()));
        });
        childProcess.stderr.on('data', function (data) {
            std_err += data.toString();
            if (stream_output)
                console.log(chalk.red(data.toString()));
        });

        childProcess.on('close', (code) => {
            if (code === 0) {
                console.log(chalk.blue(`exit_code = ${code}`));
                return resolve(std_out);
            }
            else {
                console.log(chalk.yellow(`exit_code = ${code}`));
                return reject(std_err);
            }
        });

        childProcess.on('error', (error) => {
            std_err += error.toString();
            if (stream_output)
                console.log(chalk.red(error.toString()));
        });
    });
}

spawn_promise('./test.sh',  { env: process.env})
   .then(() => {
   });

Output

 $ node test.js 
./test.sh [[object Object]]

<stuck here>

我修改您的代码以包括以下内容:

...
        childProcess.stdout.on('data', function (data) {


            if (data == "Please enter something:\n")
            {
                childProcess.stdin.write("something\n");
                //childProcess.stdin.end(); // Call this to end the session
            }

            std_out += data.toString();
            if (stream_output)
                console.log(chalk.green(data.toString()));
        });
  ...

然后我再次跑步:

$ node test.js 
./test.sh [[object Object]]
exit_code = 0

有效。基本上,您需要找出stdin等待输入的时间。您可以为此在data上使用stdout事件,然后在stdin上进行写入。如果没有要写的凭据,可以通过调用childProcess.stdin.end();

结束会话