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

Node.js 子进程提示输入时,child_process.spawn()挂起,node.js,child-process,Node.js,Child Process,我已经编写了一个节点脚本来管理git存储库到AWS自动缩放组的部署 该脚本使用child_process.spawn()来自动化git、克隆存储库、签出标记等 如果git能够找到合适的凭据,它就可以正常工作。但是,如果没有自动找到凭据,则生成的进程将尝试提示输入凭据,此时将挂起。即使Ctrl-C也无法退出。整个shell会话必须结束 spawn()调用封装在一个函数中以返回承诺。我的函数看起来是这样的 const cp = require('child_process'); let spawn

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

该脚本使用child_process.spawn()来自动化git、克隆存储库、签出标记等

如果git能够找到合适的凭据,它就可以正常工作。但是,如果没有自动找到凭据,则生成的进程将尝试提示输入凭据,此时将挂起。即使Ctrl-C也无法退出。整个shell会话必须结束

spawn()调用封装在一个函数中以返回承诺。我的函数看起来是这样的

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(() => {
      ...
我这样称呼它

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提示输入凭据,如果git在用户环境中找不到凭据,则抛出一个错误。然而,这并不理想。理想情况下,我希望能够优雅地处理标准输入,并且仍然能够像目前这样捕获和处理输出和错误

我可以通过这样做来解决输入的问题

let childProcess = cp.spawn(command, args, { stdio: [process.stdin, process.stdout, process.stderr] });
这允许git正确提示输入凭据。但是,我将失去捕获命令输出的能力

正确的处理方法是什么


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

子进程有
stdin
来处理输入,并且在运行
子进程
时也可以使用stdin来输入

请参见下面的示例:

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(() => {
   });
输出

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

<stuck here>
然后我再次跑:

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

它起作用了。基本上,您需要知道stdin何时等待输入。您可以在
stdout
上使用
data
事件,然后在
stdin
上写入。如果您没有要写入的凭据,可以通过调用
childProcess.stdin.end()来结束会话

这里有同样的问题:idk如果这有帮助,因为我没有尝试任何类似的方法,但是您可以使用它将凭证从stdio输入转发到子进程谢谢您的建议。这确实让我更接近一个解决方案。但是,是否有任何方法可以在不需要硬编码提示文本的情况下处理它?在我看来,这会使代码变得脆弱。如果子进程以任何方式发生更改,或者提示输入其他内容,那么我的代码将再次失败。我真的需要一些更通用的东西,这样它可以用来生成各种进程,而不必知道子进程可能会提示什么。您正在处理交互式shell,因此如果子进程需要一些输入,它将提示。可以创建一个计时器来监视子进程的状态。如果进程受阻,您可以终止它。