我编写了一个节点脚本来管理将 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"参数的用途。我希望能够实时查看命令的输出,而不是等到该过程完成。
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("somethingn");
//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 何时等待输入。您可以在stdout
上使用data
事件,然后在stdin
上写入。如果您没有要写入的凭据,可以通过调用childProcess.stdin.end();
来结束会话