Skip to content

Commit c0d9cf7

Browse files
committed
Merge pull request #335 from shelljs/more-exec-options
feat(exec): allow all exec options to pass through
2 parents 3c89df7 + 164a214 commit c0d9cf7

File tree

4 files changed

+70
-27
lines changed

4 files changed

+70
-27
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,8 @@ Available options (all `false` by default):
487487
+ `async`: Asynchronous execution. If a callback is provided, it will be set to
488488
`true`, regardless of the passed value.
489489
+ `silent`: Do not echo program output to console.
490+
+ and any option available to NodeJS's
491+
[child_process.exec()](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)
490492

491493
Examples:
492494

src/exec.js

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ var path = require('path');
55
var fs = require('fs');
66
var child = require('child_process');
77

8+
var DEFAULT_MAXBUFFER_SIZE = 20*1024*1024;
9+
810
// Hack to run child_process.exec() synchronously (sync avoids callback hell)
911
// Uses a custom wait loop that checks for a flag file, created when the child process is done.
1012
// (Can't do a wait loop that checks for internal Node variables/messages as
@@ -18,15 +20,18 @@ function execSync(cmd, opts) {
1820
scriptFile = path.resolve(tempDir+'/'+common.randomFileName()),
1921
sleepFile = path.resolve(tempDir+'/'+common.randomFileName());
2022

21-
var options = common.extend({
22-
silent: common.config.silent
23+
opts = common.extend({
24+
silent: common.config.silent,
25+
cwd: _pwd(),
26+
env: process.env,
27+
maxBuffer: DEFAULT_MAXBUFFER_SIZE
2328
}, opts);
2429

2530
var previousStdoutContent = '',
2631
previousStderrContent = '';
2732
// Echoes stdout and stderr changes from running process, if not silent
2833
function updateStream(streamFile) {
29-
if (options.silent || !fs.existsSync(streamFile))
34+
if (opts.silent || !fs.existsSync(streamFile))
3035
return;
3136

3237
var previousStreamContent,
@@ -58,19 +63,13 @@ function execSync(cmd, opts) {
5863
if (fs.existsSync(codeFile)) common.unlinkSync(codeFile);
5964

6065
var execCommand = '"'+process.execPath+'" '+scriptFile;
61-
var execOptions = {
62-
env: process.env,
63-
cwd: _pwd(),
64-
maxBuffer: 20*1024*1024
65-
};
66-
6766
var script;
6867

6968
if (typeof child.execSync === 'function') {
7069
script = [
7170
"var child = require('child_process')",
7271
" , fs = require('fs');",
73-
"var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {",
72+
"var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: "+opts.maxBuffer+"}, function(err) {",
7473
" fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');",
7574
"});",
7675
"var stdoutStream = fs.createWriteStream('"+escape(stdoutFile)+"');",
@@ -88,28 +87,28 @@ function execSync(cmd, opts) {
8887

8988
fs.writeFileSync(scriptFile, script);
9089

91-
if (options.silent) {
92-
execOptions.stdio = 'ignore';
90+
if (opts.silent) {
91+
opts.stdio = 'ignore';
9392
} else {
94-
execOptions.stdio = [0, 1, 2];
93+
opts.stdio = [0, 1, 2];
9594
}
9695

9796
// Welcome to the future
98-
child.execSync(execCommand, execOptions);
97+
child.execSync(execCommand, opts);
9998
} else {
10099
cmd += ' > '+stdoutFile+' 2> '+stderrFile; // works on both win/unix
101100

102101
script = [
103102
"var child = require('child_process')",
104103
" , fs = require('fs');",
105-
"var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {",
104+
"var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: "+opts.maxBuffer+"}, function(err) {",
106105
" fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');",
107106
"});"
108107
].join('\n');
109108

110109
fs.writeFileSync(scriptFile, script);
111110

112-
child.exec(execCommand, execOptions);
111+
child.exec(execCommand, opts);
113112

114113
// The wait loop
115114
// sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage
@@ -156,24 +155,27 @@ function execAsync(cmd, opts, callback) {
156155
var stdout = '';
157156
var stderr = '';
158157

159-
var options = common.extend({
160-
silent: common.config.silent
158+
opts = common.extend({
159+
silent: common.config.silent,
160+
cwd: _pwd(),
161+
env: process.env,
162+
maxBuffer: DEFAULT_MAXBUFFER_SIZE
161163
}, opts);
162164

163-
var c = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) {
165+
var c = child.exec(cmd, opts, function(err) {
164166
if (callback)
165167
callback(err ? err.code : 0, stdout, stderr);
166168
});
167169

168170
c.stdout.on('data', function(data) {
169171
stdout += data;
170-
if (!options.silent)
172+
if (!opts.silent)
171173
process.stdout.write(data);
172174
});
173175

174176
c.stderr.on('data', function(data) {
175177
stderr += data;
176-
if (!options.silent)
178+
if (!opts.silent)
177179
process.stderr.write(data);
178180
});
179181

@@ -187,6 +189,8 @@ function execAsync(cmd, opts, callback) {
187189
//@ + `async`: Asynchronous execution. If a callback is provided, it will be set to
188190
//@ `true`, regardless of the passed value.
189191
//@ + `silent`: Do not echo program output to console.
192+
//@ + and any option available to NodeJS's
193+
//@ [child_process.exec()](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)
190194
//@
191195
//@ Examples:
192196
//@
@@ -233,9 +237,13 @@ function _exec(command, options, callback) {
233237
async: false
234238
}, options);
235239

236-
if (options.async)
237-
return execAsync(command, options, callback);
238-
else
239-
return execSync(command, options);
240+
try {
241+
if (options.async)
242+
return execAsync(command, options, callback);
243+
else
244+
return execSync(command, options);
245+
} catch (e) {
246+
common.error('internal error');
247+
}
240248
}
241249
module.exports = _exec;

test/exec.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
var shell = require('..');
22

3-
var assert = require('assert'),
4-
util = require('util');
3+
var assert = require('assert');
4+
var util = require('util');
5+
var path = require('path');
6+
var os = require('os');
57

68
shell.config.silent = true;
79

@@ -79,6 +81,32 @@ assert.equal(shell.error(), null);
7981
assert.equal(result.code, 0);
8082
assert.equal(result.stdout, "'+'_'+'\n");
8183

84+
// set cwd
85+
var cmdString = process.platform === 'win32' ? 'cd' : 'pwd';
86+
result = shell.exec(cmdString, {cwd: '..'});
87+
assert.equal(shell.error(), null);
88+
assert.equal(result.code, 0);
89+
assert.equal(result.stdout, path.resolve('..') + os.EOL);
90+
91+
// set maxBuffer (very small)
92+
result = shell.exec('echo 1234567890'); // default maxBuffer is ok
93+
assert.equal(shell.error(), null);
94+
assert.equal(result.code, 0);
95+
assert.equal(result.stdout, '1234567890' + os.EOL);
96+
if (process.version >= 'v0.11') { // this option doesn't work on v0.10
97+
shell.exec('echo 1234567890', {maxBuffer: 6});
98+
assert.ok(shell.error());
99+
}
100+
101+
// set timeout option
102+
result = shell.exec('node resources/exec/slow.js'); // default timeout is ok
103+
assert.ok(!shell.error());
104+
assert.equal(result.code, 0);
105+
if (process.version >= 'v0.11') { // this option doesn't work on v0.10
106+
result = shell.exec('node resources/exec/slow.js', {timeout: 10}); // times out
107+
assert.ok(shell.error());
108+
}
109+
82110
//
83111
// async
84112
//

test/resources/exec/slow.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env node
2+
// sleep for 5 seconds
3+
setTimeout(function() {
4+
console.log('slow');
5+
}, 100);

0 commit comments

Comments
 (0)