【问题标题】:How to execute shell commands synchronously in Node?如何在 Node 中同步执行 shell 命令?
【发布时间】:2020-05-05 18:33:29
【问题描述】:

我正在尝试同步执行一些 shell 命令来安装 npm 依赖项、构建包并在 docker 中创建数据库。

    ['api', 'front-end'].forEach(async (dir) => {
        await new Promise((resolve, reject) => {
          console.log(`Installing npm dependencies for ${dir}`);
          exec('npm install', { cwd: path.join(initDir, 'pushkin', dir) }, (err) => {
            if (err) console.error(`Failed to install npm dependencies for ${dir}: ${err}`);
            if (dir !== 'api' && dir !== 'front-end') return;
          });
          resolve(`${dir} installed...`);
        })
          .then(() => {
            console.log(`Building ${dir}`);
            exec('npm run build', { cwd: path.join(process.cwd(), 'pushkin', dir) }, (err) => {
              if (err) console.error(`Failed to build ${dir}: ${err}`);
              console.log(`${dir} is built`);
            });
          })
          .then(() => {
            shell.exec(startDbCommand);
          })
          .then(() => {
            shell.exec(createDbCommand);
          })
          .then(() => {
            shell.exec(stopDbCommand);
          });
      });

docker 命令是:

const startDbCommand = 'docker-compose -f pushkin/docker-compose.dev.yml up --no-start && docker-compose -f pushkin/docker-compose.dev.yml start test_db';
const createDbCommand = 'docker-compose -f pushkin/docker-compose.dev.yml exec -T test_db psql -U postgres -c "create database test_db"';
const stopDbCommand = 'docker-compose -f pushkin/docker-compose.dev.yml stop test_db';

当我第一次运行它时,我得到了这个错误:

No container found for test_db_1

Failed to build front-end: Error: Command failed: npm run build
sh: react-scripts: command not found

Failed to build api: Error: Command failed: npm run build
sh: babel: command not found

但是,在我第二次再次运行它之后,一切似乎都很好。这是我写的 Promise 链的问题吗?谢谢。

【问题讨论】:

  • 同步?你的问题有点混乱,你使用的承诺,你的意思是异步的。如果你想要同步,你会使用execSync
  • Promise 不会使任何东西同步。他们可以帮助制作异步的东西顺序虽然
  • 你能发布你的 Dockerfile 吗?
  • 我不知道你为什么要在 nodejs 中这样做?为什么不运行 dockerfile / docker-compose 文件中的所有内容?

标签: javascript node.js docker


【解决方案1】:

两个重要的事情是依次运行命令(我相信这就是您所说的同步的意思?)以及在出现故障时退出。

项目目录循环也显得格格不入。目前它会遍历所有内容,包括 db setup 命令。看起来你正在做测试设置,所以我相信“同步”顺序是:

  • npm install/buildapi
  • npm install/buildfrontend
  • 数据库设置

首先,从节点spawn 创建一个promise,这样你就可以await 它。

function runProcessToCompletion(cmd_array, options){
  return new Promise((resolve, reject) => {

    const result = {
      cmd: cmd_array,
      options,
      code: null,
      output: [],
    }

    const proc = spawn(cmd_array[0], cmd_array.slice(1), options)

    proc.on('error', (error) => {
      error.result = result
      reject(error)
    })

    proc.on('close', code => {
      result.code = code
      if (code !== 0) {
        const error = new Error(`PID "${proc.pid}" exited with code "${code}"`)
        error.result = result
        reject(error)
      }
      console.log(`Spawned PID "${proc.pid}" exited with code "${code}"`)
      resolve(result)
    })

    proc.stdout.on('data', (data) => {
      result.output.push(data.toString())
      process.stdout.write(data)
    })

    proc.stderr.on('data', (data) => {
      result.output.push(data.toString())
      process.stderr.write(data)
    })

    if (proc.pid) {
      console.log(`Spawned PID "${proc.pid}" for "${cmd_array.join(' ')}"`)
    }
  })
}

然后,您可以更轻松地将代码构建为简单的命令列表。 使用spawn 的好处是可以避免所有的shell 主义。 缺点是你错过了所有的 shell-isms。

例如,可执行文件的路径需要在没有 shell 的情况下完全定义 PATH

const path = require('path')
const initDir = process.cwd()
const project_dirs = ['api', 'front-end']
const setupDbCommand = ['/usr/local/bin/docker-compose','-f','pushkin/docker-compose.dev.yml','up','--no-start']
const startDbCommand = ['/usr/local/bin/docker-compose','-f','pushkin/docker-compose.dev.yml','start','test_db']
const createDbCommand = ['/usr/local/bin/docker-compose','-f','pushkin/docker-compose.dev.yml','exec','-T','test_db','psql -U postgres -c "create database test_db"']
const stopDbCommand = ['/usr/local/bin/docker-compose','-f','pushkin/docker-compose.dev.yml','stop','test_db']

async function go(){
  for (let dir of project_dirs) {
    const cwd = path.join(initDir, 'pushkin', dir)
    await runProcessToCompletion(['/usr/local/bin/npm','install'], { cwd })
    await runProcessToCompletion(['/usr/local/bin/npm','run','build'], { cwd })
  }
  await runProcessToCompletion(setupDbCommand)
  await runProcessToCompletion(startDbCommand)
  await runProcessToCompletion(createDbCommand)
  await runProcessToCompletion(stopDbCommand)
  return true
}

go().catch(err => {
  console.error(err)
  console.error(err.results)
})

如果没有外壳的东西太难了,你可以用spawn options重新打开它

{ shell: true }

【讨论】:

    猜你喜欢
    • 2015-11-20
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 2017-12-26
    • 2013-04-01
    • 2013-09-17
    • 2021-01-14
    相关资源
    最近更新 更多