spawn

18 阅读1分钟

spawn

spawn 是什么

child_process 模块中的一个函数,用来

  • 启动一个子进程
  • 执行某个命令
  • 实时捕获输出(stdout/stderr)
  • 与子进程交互(通过 stdin)

    spawn 适合执行需要持续交互或输出的命令,比如运行服务器、构建工具等。spawn 想成 Node 里 终端代理人

基本使用

导入

import { spawn } from 'child_process';

执行命令 node ./spawn.mjs

// spawn.mjs
const command = 'ls -la';
const cwd = process.cwd(); // 当前工作目录
const [cmd, ...args] = command.split(' ');
const child = spawn(cmd, args, {
  cwd,
  stdio: 'inherit', //实时输出到控制台
  shell: true,
});
child.on('close', (code) => {
  console.log(`子进程已结束,退出码 ${code}`);
});
  • command: 要执行的命令字符串

  • cwd: 当前工作目录

  • stdio: 'inherit' 表示子进程的输入输出与父进程共享,实时显示输出

  • shell: true 表示通过 shell 执行命令,允许使用管道等 shell 特性

  • close 事件: 当子进程结束时触发,code 是退出码

image-2.png

监听日志

const child = spawn('pnpm', ['dev']);

child.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

child.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});
child.on('close', (code) => {
  console.log(`子进程已结束,退出码 ${code}`);
});
  • child.stdout.on('data'): 监听标准输出数据
  • child.stderr.on('data'): 监听错误输出数据
  • child.on('close'): 监听子进程结束事件

输出

stdout: > react-todo-app@0.0.1 dev
stdout: > vite

stdout:   VITE v5.0.8  ready in 550 ms

stdout:   ➜  Local:   http://localhost:5173/
stdout:   ➜  Network: http://192.168.31.88:5173/
stdout:   ➜  press h to show help

错误案例输出

stderr:  ✘ [ERROR] Unexpected token

stderr:     src/components/TodoItem.tsx:17:14:
stderr:       17 │     return <div>{todo.title}</div>
stderr:          │               ^
stderr:
stderr: 1 error

输入内容(与子进程交互(通过 stdin))

const child1 = spawn('bash', {
  stdio: 'pipe',
  shell: true,
});
child1.stdin.write('ls -la\n');
child1.stdin.write('pwd\n');
child1.stdin.write('echo "Hello from child process"\n');
child1.stdin.end();

child1.stdout.on('data', (data) => {
  console.log(`子进程输出: ${data}`);
});

child1.stderr.on('data', (data) => {
  console.error(`子进程错误输出: ${data}`);
});

child1.on('close', (code) => {
  console.log(`子进程已结束,退出码 ${code}`);
});
  • stdio: 'pipe' 表示父子进程通过管道通信
  • child1.stdin.write(...): 向子进程的标准输入写入命令,此时 stdio: 'inherit' 不适用 , 因为需要单独操作 stdin
  • child1.stdin.end(): 结束输入,告诉子进程没有更多数据了

stdio: 'inherit' 时,stdin 是继承自父进程的,不能单独写入数据,就不能再对 stdin/stdout/stderr 做手动监听或写入——包括:

child.stdin.write(...)
child.stdout.on('data')
child.stderr.on('data')

会出现如下报错

TypeError: Cannot read property 'write' of null

当使用管道符号时,必须使用 shell: true

const command =
  'echo -e "n\nn" | pnpm create vite react-todo-app --template react-ts';