[英]Is there a way to run Node.js with Bash commands in Windows?
[英]Run bash in node js
我需要启动bash
终端,依次执行几个命令,收集它们的结果并退出。 在nodejs中执行此操作的正确方法是什么?
我尝试使用child_process.spawn
来实现这一点,但即使使用单个命令,它也不能像我预期的那样工作。
这是简化的代码:
const process = spawn(`bash`, [])
// wait for the process to spawn
await new Promise<void>(resolve => process.once(`spawn`, resolve))
// log any output (expected to be the current node version)
process.stdout.on(`data`, data => console.log(data))
// wait for "node --version" to execute
await new Promise<void>(resolve => process.stdin.write(`node --version\n`, `utf8`, () => resolve()))
// wait for the process to end
await new Promise<void>(resolve => process.once(`close`, resolve))
这里的问题是我没有在stdout
中收到任何输出,而await process.once('spawn')
工作正常。
我已经记录了stderr
和所有其他事件,如process.on
和stdout.on('error')
但它们都是空的。 所以我想知道这里有什么问题。
此外,谷歌有大量关于如何运行单个命令的示例。 但我需要在同一个终端中运行多个,在每次调用之间等待并从stdout
收集单独的结果。 如果单个命令无法按预期工作,我不确定如何执行此操作。
使用shelljs
package 并实现一个助手 function 来运行每个命令。 助手 function:
const shell = require('shelljs');
async function runShellCmd(cmd) {
return new Promise((resolve, reject) => {
shell.exec(cmd, async (code, stdout, stderr) => {
if (!code) {
return resolve(stdout);
}
return reject(stderr);
});
});
}
// run your commands here
如果在一个实例中运行多个命令,我将使用.sh
脚本 go :
node file.js
进行其他处理(例如计算、执行其他命令、异步/等待查询),然后通过文件或环境变量导出环境所需的值以继续 有两件事可能会导致问题。 首先是最后一个代码立即执行resolve()
允许代码执行立即移至下一条指令。 其次是console.log(data)
可能不足以打印 output。 正如我所观察到的, data
是一个Buffer
,而不是一个字符串。
首先,提前执行带有未知参数的脚本听起来像是在使用用户输入,这会使您面临许多漏洞。 还要始终使用绝对路径以避免路径中毒。 但是让我们假设你正确地消毒了所有东西。 您可以将-c
参数与 bash 一起使用来指定要运行的内容并用;
分隔命令。 s。
根据 man 文件:
如果存在 -c 选项,则从第一个非选项参数 command_string 中读取命令。 如果在 command_string 之后有 arguments,则第一个参数分配给 $0,其余的 arguments 分配给位置参数。 对 $0 的赋值设置 shell 的名称,用于警告和错误消息。
这是一个对我来说很好的例子
#!/usr/bin/node
const {spawn} = require('child_process');
const magicString = '#@*$&#@*%&#@(#%@%#@';
const myScript = 'which_node=$(which node);' +
'node_ver=$($which_node --version);' +
'echo "where = $which_node";' +
'echo "version = $node_ver";' +
`echo '${magicString}';` +
'read foobar;' +
'echo $foobar;';
let cp = spawn('/usr/bin/bash', ['-c',myScript]);
cp.stdout.setEncoding('utf8');
let data = '';
cp.stdout.on('data',(d) => {
if(d.endsWith(magicString+'\n')) {
d = d.substring(0,d.length-magicString.length-1);
data += d;
// do whatever with data
cp.stdin.write('asdfasdsadfasdf\n');
}
else
data += d;
});
cp.on('close',()=>console.log(data));
Output 如预期:
$ ./test.js
where = /usr/local/bin/node
version = v14.18.1
asdfasdsadfasdf
当我按照spawn()
文档提供的示例进行操作时,一切似乎都正常。
const { spawn } = require('child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); ls.on('close', (code) => { console.log(`child process exited with code ${code}`); });
我们可以修改此示例以使用bash
而不是ls
。 然后给它一些命令(通过stdin.write()
)和end()
stream,所以 bash 知道我们已经写完了。
const { spawn } = require('child_process');
const bash = spawn('bash');
bash.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
bash.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
bash.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
bash.stdin.write('node --version\n');
bash.stdin.write('npm --version\n');
bash.stdin.end();
当您运行此脚本时,它会生成(版本号可能不同):
stdout: v16.13.1
stdout: 8.1.2
child process exited with code 0
空行的原因是命令输出在末尾包含一个换行符, console.log()
在顶部添加了一个额外的换行符。
我很困惑为什么你会 go 通过bash
进程来完成这一切,而 bash 最终会创建不同的子进程。 感觉就像你让事情变得比他们需要的更复杂。
使用exec()
会容易得多。
const util = require('util');
const exec = util.promisify(require('child_process').exec);
async function main() {
const { stdout: nodeVersion } = await exec('node --version');
console.log("node version:", nodeVersion);
const { stdout: npmVersion } = await exec('npm --version');
console.log("npm version:", npmVersion);
}
main();
产生:
node version: v16.13.1
npm version: 8.1.2
我不确定问题是什么,因为我遵循了文档,添加了 function 返回 promise 并且它似乎有效
let {spawn}=require('child_process') //spawn module
async function exec(command){
return await new Promise(resolve=>{
var toReturn="" //text to return
let options={env:process.env,cwd:undefined,shell:true}
let myChild=spawn(command,options)
myChild.stdout.on('data',txt=>toReturn+=txt)
myChild.stderr.on('data',txt=>toReturn+=txt)
myChild.on('close',()=>resolve(toReturn))
})
};
(async()=>{
console.log(await exec('ls\nrm --help')) //2 commands in one child process
//exec('bash some_file_full_of_bash_commands.txt') //also works
})()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.