222
schangxiang@126.com
2025-06-13 6a8393408d8cefcea02b7a598967de8dc1e565c2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
'use strict';
 
const debug = require('debug')('runscript');
const is = require('is-type-of');
const assert = require('assert');
const path = require('path');
const spawn = require('child_process').spawn;
 
/**
 * Run shell script in child process
 * Support OSX, Linux and Windows
 * @param {String} script - full script string, like `git clone https://github.com/node-modules/runscript.git`
 * @param {Object} [options] - spawn options
 *   @see https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
 * @return {Object} stdio object, will contains stdio.stdout and stdio.stderr buffer.
 */
module.exports = function runScript(script, options) {
  return new Promise((resolve, reject) => {
    options = options || {};
    options.env = options.env || Object.create(process.env);
    options.cwd = options.cwd || process.cwd();
    options.stdio = options.stdio || 'inherit';
    if (options.stdout) assert(is.writableStream(options.stdout), 'options.stdout should be writable stream');
    if (options.stderr) assert(is.writableStream(options.stderr), 'options.stderr should be writable stream');
 
    let sh = 'sh';
    let shFlag = '-c';
 
    if (process.platform === 'win32') {
      sh = process.env.comspec || 'cmd';
      shFlag = '/d /s /c';
      options.windowsVerbatimArguments = true;
      if (script.indexOf('./') === 0 || script.indexOf('.\\') === 0 ||
          script.indexOf('../') === 0 || script.indexOf('..\\') === 0) {
        const splits = script.split(' ');
        splits[0] = path.join(options.cwd, splits[0]);
        script = splits.join(' ');
      }
    }
 
    debug('%s %s %s, %j', sh, shFlag, script, options);
    const proc = spawn(sh, [ shFlag, script ], options);
    const stdout = [];
    const stderr = [];
    if (proc.stdout) {
      proc.stdout.on('data', buf => {
        stdout.push(buf);
      });
      if (options.stdout) {
        proc.stdout.pipe(options.stdout);
      }
    }
    if (proc.stderr) {
      proc.stderr.on('data', buf => {
        stderr.push(buf);
      });
      if (options.stderr) {
        proc.stderr.pipe(options.stderr);
      }
    }
    proc.on('error', reject);
    proc.on('close', code => {
      const stdio = {
        stdout: null,
        stderr: null,
      };
      if (stdout.length > 0) {
        stdio.stdout = Buffer.concat(stdout);
      }
      if (stderr.length > 0) {
        stdio.stderr = Buffer.concat(stderr);
      }
      if (code !== 0) {
        const err = new Error(`Run "${sh} ${shFlag} ${script}" error, exit code ${code}`);
        err.stdio = stdio;
        return reject(err);
      }
      return resolve(stdio);
    });
  });
};