繁体   English   中英

在节点js中运行bash

[英]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.onstdout.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 :

  • 您也可以将一个命令的 output 用于下一个命令。 我在这里找到了一个例子: https://linuxhint.com/bash_command_output_variable/
  • 您还可以运行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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM