-
Notifications
You must be signed in to change notification settings - Fork 722
/
cmd.js
142 lines (125 loc) · 4.25 KB
/
cmd.js
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
var common = require('./common');
var _tempDir = require('./tempdir');
var _which = require('./which');
var path = require('path');
var fs = require('fs');
var child = require('child_process');
common.register('cmd', _cmd, {
canReceivePipe: true,
wrapOutput: false,
cmdOptions: null,
});
// Similar to shell.exec(), this is a hack so that we can get concurrent output.
function cmdSync(cmd, args, opts, pipe) {
var tempDir = _tempDir();
var stdoutFile = path.join(tempDir, common.randomFileName());
var stderrFile = path.join(tempDir, common.randomFileName());
opts = common.extend({
silent: common.config.silent,
cwd: process.cwd(),
}, opts);
if (fs.existsSync(stdoutFile)) common.unlinkSync(stdoutFile);
if (fs.existsSync(stderrFile)) common.unlinkSync(stderrFile);
// resolve to an absolute path, so if we cd in the child process, it's a no-op
opts.cwd = path.resolve(opts.cwd);
var optString = JSON.stringify(opts);
function cleanUpFiles() {
try { common.unlinkSync(stdoutFile); } catch (e2) {}
try { common.unlinkSync(stderrFile); } catch (e2) {}
}
opts.stdio = [0, 1, 2];
var proc;
try {
proc = child.spawnSync(common.nodeBinPath, [
path.join(__dirname, 'child.js'),
stdoutFile,
stderrFile,
cmd,
JSON.stringify(args),
optString,
pipe,
], opts);
} catch (e) {
// Clean up immediately if we have an exception
cleanUpFiles();
throw e;
}
var stdout = fs.readFileSync(stdoutFile, 'utf8');
var stderr = fs.readFileSync(stderrFile, 'utf8');
var code = proc.status;
// Clean up files (we can delay this to improve performance)
setTimeout(cleanUpFiles, 0);
if (code !== 0) {
common.error(stderr, code, true);
}
// common.state.error = stderr;
// common.state.errorCode = code;
// return stdout;
return new common.ShellString(stdout, stderr, code);
} // cmdSync()
//@
//@ ### cmd(command [, arguments...] [, options] [, callback])
//@ Available options (all `false` by default):
//@
//@ + `silent`: Do not echo program output to console.
//@ + and any option available to NodeJS's
//@ [child_process.spawnSync()](https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options)
//@
//@ Examples:
//@
//@ ```javascript
//@ var version = cmd('node', '--version', {silent:true}).stdout;
//@
//@ cmd('git', 'checkout', 'master', '--', 'file.js');
//@ ```
//@
//@ An alternative for [exec()](#execcommand--options--callback), with better
//@ security around globbing, comamnd injection, and variable expansion. This is
//@ guaranteed to only run one external command, and won't handle special
//@ characters in unexpected and unsafe ways.
//@
//@ By default, this performs globbing on all platforms (but you can disable
//@ this for extra security using `set('-f')`).
function _cmd() {
var args = [].slice.call(arguments, 1); // ignore the options at the start
var command;
var cmdArgs = [];
var options = {};
if (args.length < 1 || typeof args[0] !== 'string') {
common.error('must specify a command to run');
} else if (args.length === 1) {
command = _which('', args[0]); // just this command, no args, no options
} else {
command = _which('', args[0]);
var lastArg = args[args.length - 1];
cmdArgs = typeof lastArg === 'string' ? args.slice(1) : args.slice(1, args.length - 1);
options = typeof lastArg === 'string' ? {} : lastArg;
}
if (!command) {
common.error('command not found: ' + args[0], 127);
}
var pipe = common.readFromPipe();
options = common.extend({
silent: common.config.silent,
}, options);
// If we're explicitly told to not offer real-time output, use a more
// efficient function
if (options.realtimeOutput === false) {
var result = child.spawnSync(command, cmdArgs, options);
if (!options.silent) {
process.stdout.write(result.stdout);
process.stderr.write(result.stderr);
}
// common.state.stderr = result.stderr;
// common.state.code = result.status;
// return stdout;
return new common.ShellString(result.stdout.toString(), result.stderr.toString(), result.status);
} else {
try {
return cmdSync(command, cmdArgs, options, pipe);
} catch (e) {
common.error('internal error');
}
}
}
module.exports = _cmd;