├── .gitignore ├── README.md ├── exec.js ├── package.json └── tests ├── bad-cmd.js ├── buffer.js ├── env-set.js ├── ls-args.js ├── ls-env.js ├── simple-ls.js └── timeout.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | exec 2 | ==== 3 | 4 | Call a child process with the ease of exec and safety of spawn 5 | 6 | 7 | **DEPRECATED:** If your version of node supports `child_process.execFile`, consider 8 | using that instead, as that does everything this module does and more... the usage 9 | is slightly different. 10 | 11 | [http://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback] 12 | (http://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback) 13 | 14 | Why? 15 | ---- 16 | 17 | This module provides the best of both worlds of `spawn` and `exec` 18 | 19 | It will callback with 2 strings containing stdout and stderr 20 | (like `child_process.exec`), but will take an array of process arguments 21 | (like `child_process.spawn`) to avoid any potentially harmful shell expansion. 22 | 23 | Usage 24 | ----- 25 | 26 | ``` js 27 | var exec = require('exec'); 28 | ``` 29 | 30 | Example 31 | ------- 32 | 33 | ``` js 34 | var exec = require('exec'); 35 | 36 | exec(['ls', '-lha'], function(err, out, code) { 37 | if (err instanceof Error) 38 | throw err; 39 | process.stderr.write(err); 40 | process.stdout.write(out); 41 | process.exit(code); 42 | }); 43 | ``` 44 | 45 | The example above will call `ls -lha` safely, by passing the arguments directly 46 | to `exec(2)` without using an shell expansion/word splitting. 47 | 48 | It returns a `child_process.spawn` object, and callbacks with any stdout, 49 | stderr, and the exit status of the command. The above example will throw an 50 | error if anything went wrong during the spawn, otherwise it will print the stdout, 51 | stderr, and exit with the exit code of `ls`. 52 | 53 | **NOTE:** If `err` is an instanceof `Error`, it means that `child_process.spawn` emitted 54 | and `error` event, and `err` is set to that error object. 55 | 56 | `err` and `out` are encoded as`utf-8` strings by default 57 | 58 | For backwards compatibility with `child_process.exec`, it is also possible 59 | to pass a string to `exec`. The string will automatically be converted to 60 | `['/bin/sh', '-c', '{string}']`, which will cause the string to be parsed on the 61 | shell. Note that if you use this method, you are at risk of shell expansion, 62 | word splitting, and other shell features that could be potentially unsafe. 63 | 64 | ``` js 65 | exec('cat foo | grep bar', function(err, out, code) { 66 | if (err instanceof Error) 67 | throw err; 68 | process.stderr.write(err); 69 | process.stdout.write(out); 70 | process.exit(code); 71 | }); 72 | ``` 73 | 74 | Functions 75 | --------- 76 | 77 | ### exec(['args'], [opts], callback) 78 | 79 | - `args`: an array of arguments to execute 80 | - `opts`: is additional options to pass to `child_process.spawn` 81 | 82 | In addition to the `child_process.spawn` options, more options have been added to mimic the behavior 83 | of `child_process.exec` 84 | 85 | - `opts.timeout`: number of milliseconds to wait for the program to complete before sending it 86 | `SIGTERM`. Note that by default, your program will wait indefinitely for the 87 | spawned program to terminate. Upon sending the fatal signal, `exec` will return 88 | with whatever stdout and stderr was produced. 89 | - `opts.killSignal`: the signal to use when `opts.timeout` is used, defaults to `SIGTERM` 90 | - `opts.encoding`: the encoding to use for stdout and stderr. **NOTE**: unlike `child_process.exec`, this defaults 91 | to `'utf-8'` if unset. Set to `'buffer'` to handle binary data. 92 | 93 | Installation 94 | ------------ 95 | 96 | npm install exec 97 | 98 | License 99 | ------- 100 | 101 | MIT 102 | -------------------------------------------------------------------------------- /exec.js: -------------------------------------------------------------------------------- 1 | var spawn = require('child_process').spawn; 2 | var util = require('util'); 3 | 4 | module.exports = util.deprecate(exec, 'exec: use child_process.execFile instead'); 5 | 6 | /** 7 | * Spawn a child with the ease of exec, but safety of spawn 8 | * 9 | * @param args array ex. [ 'ls', '-lha' ] 10 | * @param opts object [optional] to pass to spawn 11 | * @param callback function(err, out, code) 12 | * 13 | * @return spawn object 14 | */ 15 | function exec(args, opts, callback) { 16 | if (arguments.length < 2) 17 | throw new Error('invalid arguments'); 18 | if (typeof opts === 'function') { 19 | callback = opts; 20 | opts = {}; 21 | } 22 | if (typeof args === 'string') { 23 | args = ['/bin/sh', '-c', args]; 24 | } 25 | opts.env = opts.env || process.env; 26 | opts.encoding = opts.encoding || 'utf-8'; 27 | opts.killSignal = opts.killSignal || 'SIGTERM'; 28 | 29 | var out; 30 | var err; 31 | var encoding; 32 | var timeout; 33 | var done = false; 34 | 35 | // check encoding 36 | if (opts.encoding !== 'buffer' && Buffer.isEncoding(opts.encoding)) { 37 | // string 38 | encoding = opts.encoding; 39 | out = ''; 40 | err = ''; 41 | } else { 42 | // buffer 43 | encoding = null; 44 | out = []; 45 | err = []; 46 | } 47 | 48 | var child = spawn(args[0], args.slice(1), opts); 49 | 50 | if (encoding) { 51 | if (child.stdout) { 52 | child.stdout.setEncoding(encoding); 53 | } 54 | if (child.stderr) { 55 | child.stderr.setEncoding(encoding); 56 | } 57 | } 58 | 59 | if (child.stdout) { 60 | child.stdout.on('data', function(data) { 61 | if (encoding) 62 | out += data; 63 | else 64 | out.push(data); 65 | }); 66 | } 67 | 68 | 69 | if (child.stderr) { 70 | child.stderr.on('data', function(data) { 71 | if (encoding) 72 | err += data; 73 | else 74 | err.push(data); 75 | }); 76 | } 77 | 78 | child.on('close', function(code) { 79 | if (done) 80 | return; 81 | done = true; 82 | 83 | if (timeout) 84 | clearTimeout(timeout); 85 | 86 | if (!encoding) { 87 | out = Buffer.concat(out); 88 | err = Buffer.concat(err); 89 | } 90 | 91 | callback(err, out, code); 92 | }); 93 | 94 | child.on('error', function(e) { 95 | if (done) 96 | return; 97 | done = true; 98 | 99 | callback(e); 100 | }); 101 | 102 | if (opts.timeout) { 103 | timeout = setTimeout(function() { 104 | child.stdout.destroy(); 105 | child.stderr.destroy(); 106 | try { 107 | child.kill(opts.killSignal); 108 | } catch(e) {} 109 | timeout = null; 110 | }, opts.timeout); 111 | } 112 | 113 | return child; 114 | } 115 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exec", 3 | "description": "Call a child process with the ease of exec and safety of spawn", 4 | "author": "Dave Eddy (http://www.daveeddy.com)", 5 | "version": "0.2.1", 6 | "repository": { 7 | "url": "https://github.com/bahamas10/node-exec.git", 8 | "type": "git" 9 | }, 10 | "main": "exec.js", 11 | "scripts" : { 12 | "test": "for f in tests/*.js; do echo \"$f\"; node \"$f\" || exit 1; echo; done; echo 'Passed'" 13 | }, 14 | "dependencies": {}, 15 | "devDependencies": {}, 16 | "optionalDependencies": {}, 17 | "bin": {}, 18 | "engines": { 19 | "node": ">= v0.9.1" 20 | }, 21 | "keywords": [ "exec", "spawn", "child", "wordsplitting", "shell" ] 22 | } 23 | -------------------------------------------------------------------------------- /tests/bad-cmd.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var exec = require('..'); 4 | 5 | exec(['this should never exist'], function(err, out, code) { 6 | assert(err instanceof Error); 7 | process.exit(0); 8 | }); 9 | -------------------------------------------------------------------------------- /tests/buffer.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var exec = require('..'); 4 | 5 | var opts = { 6 | encoding: 'buffer' 7 | }; 8 | 9 | exec(['ls', '-lh', '-a'], opts, function(err, out, code) { 10 | if (err instanceof Error) 11 | throw err; 12 | assert(Buffer.isBuffer(err)); 13 | assert(Buffer.isBuffer(out)); 14 | process.exit(code); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/env-set.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var exec = require('..'); 4 | 5 | process.env.FOO = 'BAR'; 6 | 7 | exec('echo "$FOO"', function(err, out, code) { 8 | if (err instanceof Error) 9 | throw err; 10 | assert.strictEqual(out.trim(), process.env.FOO); 11 | process.stderr.write(err); 12 | process.stdout.write(out); 13 | process.exit(code); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/ls-args.js: -------------------------------------------------------------------------------- 1 | var exec = require('..'); 2 | 3 | exec(['ls', '-lh', '-a'], function(err, out, code) { 4 | if (err instanceof Error) 5 | throw err; 6 | process.stderr.write(err); 7 | process.stdout.write(out); 8 | process.exit(code); 9 | }); 10 | -------------------------------------------------------------------------------- /tests/ls-env.js: -------------------------------------------------------------------------------- 1 | var exec = require('..'); 2 | 3 | exec(['ls', '-lh', '-a'], {cwd: '/'}, function(err, out, code) { 4 | if (err instanceof Error) 5 | throw err; 6 | process.stderr.write(err); 7 | process.stdout.write(out); 8 | process.exit(code); 9 | }); 10 | -------------------------------------------------------------------------------- /tests/simple-ls.js: -------------------------------------------------------------------------------- 1 | var exec = require('..'); 2 | 3 | exec(['ls'], function(err, out, code) { 4 | if (err instanceof Error) 5 | throw err; 6 | process.stderr.write(err); 7 | process.stdout.write(out); 8 | process.exit(code); 9 | }); 10 | -------------------------------------------------------------------------------- /tests/timeout.js: -------------------------------------------------------------------------------- 1 | var exec = require('..'); 2 | 3 | var opts = { 4 | timeout: 1000 5 | }; 6 | 7 | exec('echo foo; sleep 30; echo bar', opts, function(err, out, code) { 8 | if (err instanceof Error) 9 | throw err; 10 | process.stderr.write(err); 11 | process.stdout.write(out); 12 | process.exit(code); 13 | }); 14 | --------------------------------------------------------------------------------