Node.js 执行 CLI 命令

nodejs_cli_commands2026-09-06 22:48:13

Node.js 执行 CLI 命令

Node.js 可以通过内置的 node:child_process 创建操作系统子进程,调用 Git、Docker 等 CLI。Node.js 负责启动和监控命令,实际工作由被调用的程序完成;目标程序仍需单独安装。

如何选择方法

方法 常见用途
spawn 持续输出日志、构建、启动命令;默认不经过 Shell
execFile 直接执行程序,完成后获取输出;默认不经过 Shell
exec 需要 Shell 管道、重定向等语法时使用
spawnSync / execFileSync 简单脚本中的同步检查,会阻塞当前 Node.js 事件循环

异步方法创建的是子进程,不是 JavaScript 工作线程。NestJS 请求处理中通常应避免等待耗时的同步命令。

使用 spawn 执行命令

将下面内容保存为 run-cli.mjs,执行 node run-cli.mjs:

import { spawn } from 'node:child_process';

function run(command, args, cwd = process.cwd()) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, {
      cwd,
      shell: false,
      stdio: 'inherit',
    });

    child.once('error', reject);
    child.once('close', (code, signal) => {
      if (code === 0) resolve();
      else reject(new Error(`命令失败:code=${code}, signal=${signal}`));
    });
  });
}

try {
  // 使用当前 Node.js 的绝对路径,无需依赖 PATH。
  await run(process.execPath, ['--version']);
} catch (error) {
  console.error(error.message);
  process.exitCode = 1;
}
  • command 和 args 分开传,不手动给数组参数添加 Shell 引号。
  • cwd 决定相对路径从哪个目录解析。
  • stdio: 'inherit' 直接显示子进程输出;需要解析输出时使用管道并持续消费。
  • error 处理无法启动程序等错误;close 处理进程结束及输出流关闭后的退出结果。

获取命令输出

import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const execute = promisify(execFile);
const { stdout } = await execute(process.execPath, ['--version'], {
  timeout: 10_000,
  maxBuffer: 1024 * 1024,
});
console.log(stdout.trim());

此类方式将输出保存在内存中。输出很大或持续不断时,使用 spawn 流式处理。

案例:Node.js 启动 Docker Compose

假设 Docker 已安装并运行、当前目录存在 compose.yaml,可以把上面的调用替换为:

await run('docker', ['compose', '-f', 'compose.yaml', 'up', '-d', 'chroma']);

调用关系:

Node.js 脚本 → Docker CLI / Compose → Docker 引擎 → Chroma 容器

命令成功不等于数据库已能接受请求,还应检查数据库健康接口。例如可由独立 Node.js 脚本启动容器,NestJS 仅通过 HTTP 使用 Chroma;这两项职责可以分开。

常见问题与安全边界

找不到命令时检查安装位置和 PATH,必要时使用可执行文件绝对路径。命令退出码为零通常代表成功,收到终止信号时 code 可能为 null。

不要将用户输入拼接到 exec 字符串。即使使用参数数组,也要限制允许的程序、参数、路径和工作目录,避免参数本身触发危险功能。网页不应暴露可执行任意命令的接口。Windows 的 .cmd/.bat 需要单独处理 Shell 调用方式。

参考

Node.js 官方 child_process 文档。示例使用通用 API;Docker 调用是应用场景,运行前需准备实际配置。

本页目录