├── .gitignore ├── HISTORY.md ├── LICENSE.md ├── README.md ├── bin └── multiexec ├── man ├── multiexec.1 ├── multiexec.1.html └── multiexec.1.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## v0.0.3 - Mar 30, 2015 2 | 3 | * Propagate SIGINT and SIGHUP signals to children 4 | * Force terminate processes on exit 5 | 6 | ## v0.0.2 - Mar 28, 2015 7 | 8 | * Update package.json metadata and Readme documentation 9 | * No actual changes 10 | 11 | ## v0.0.1 - Mar 28, 2015 12 | 13 | * Initial release. 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rico Sta. Cruz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multiexec 2 | 3 | **Run 2 or more commands in parallel.** 4 | 5 | ``` 6 | npm install -g multiexec 7 | ``` 8 | 9 | ``` 10 | multiexec "command 1" "command 2" 11 | ``` 12 | 13 |
14 | 15 | ## Practical use 16 | 17 | Use `multiexec` to use [browser-sync](http://browsersync.io/) with another build process. In this example, we'll use [Jekyll](http://jekyllrb.com/)—this effectively makes for a LiveReload-enabled Jekyll dev environment. 18 | 19 | ``` 20 | $ multiexec "jekyll build --watch" "browser-sync start --server _site --files='_site/*, _site/*/*'" 21 | 22 | 1 running jekyll build --watch 23 | 2 running broswer-sync start --server ... 24 | 2 [BS] Access URLs: 25 | 2 -------------------------------------- 26 | 2 Local: http://localhost:3005 27 | 2 External: http://192.168.1.187:3005 28 | 2 -------------------------------------- 29 | 2 [BS] Serving files from: _site 30 | 2 [BS] Watching files... 31 | 1 Configuration file: /home/me/project/_config.yml 32 | 1 Source: /Uome/me/project 33 | 1 Destination: /Uome/me/project/_site 34 | 1 Generating... done. 35 | ``` 36 | 37 | If you dev environment has multiple processes running together, you can use `multiexec` to run them both under one process. In this example, we'll run [Rails](http://rubyonrails.org/)'s development server with [Guard](rubygems.org/gems/guard) for auto-reloading. 38 | 39 | ``` 40 | multiexec "bundle exec rails s" "bundle exec guard" 41 | ``` 42 | 43 |
44 | 45 | ## Thanks 46 | 47 | MIT license 48 | -------------------------------------------------------------------------------- /bin/multiexec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var spawn = require('child_process').spawn; 3 | var split = require('split'); 4 | var args = process.argv.slice(2); 5 | 6 | if (args.length === 0) { 7 | var base = require('path').basename(process.argv[1]); 8 | console.log("Usage: " + base + " [ ...]"); 9 | process.exit(1); 10 | } 11 | 12 | if (args.join(' ') === '--help') { 13 | man('../man/multiexec.1'); 14 | process.exit(0); 15 | } 16 | 17 | runAll(args); 18 | 19 | /* 20 | * run many commands 21 | * runAll(['ls', 'sleep 3']); 22 | */ 23 | 24 | function runAll (commands) { 25 | var procs = commands.map(run); 26 | 27 | ['SIGTERM', 'SIGHUP', 'SIGBREAK', 'SIGINT', 'SIGWINCH'].forEach(function (signal) { 28 | process.on(signal, function () { 29 | console.log("\n"); 30 | print('!', '' + signal + ' received', 31); 31 | procs.forEach(function (proc) { proc.kill(signal); }); 32 | }); 33 | }); 34 | 35 | process.on('exit', function () { 36 | procs.forEach(function (proc) { proc.kill('SIGKILL'); }); 37 | }); 38 | } 39 | 40 | /* 41 | * run a command 42 | * run('ls', 0); 43 | */ 44 | 45 | function run (cmd, i) { 46 | log("running: " + cmd, 1); 47 | 48 | var proc = spawn("sh", ["-c", cmd]); 49 | 50 | proc.on('error', function(err) { 51 | log("error: " + err.code); 52 | }); 53 | 54 | proc.stdout 55 | .pipe(split()) 56 | .on('data', function (line) { log(line); }); 57 | 58 | proc.stderr 59 | .pipe(split()) 60 | .on('data', function (line) { log(line, 31); }); 61 | 62 | proc.on('exit', function(code) { 63 | if (code > 0) { 64 | log("exit with code " + code + ", aborting", '1;31'); 65 | process.exit(code); 66 | } else { 67 | log("exiting", 1); 68 | } 69 | }); 70 | 71 | function log(str, color) { 72 | print(i, str, color); 73 | } 74 | 75 | return proc; 76 | } 77 | 78 | /* 79 | * printing helper 80 | */ 81 | 82 | function print(i, str, color) { 83 | if (!color) color = 0; 84 | var c = typeof i === 'number' ? (i % 6) + 32 : 1; 85 | console.log("\033["+c+"m" + i + "\033[0m\033[0" + color + "m " + str + "\033[0m"); 86 | } 87 | 88 | /* 89 | * opens a man page 90 | * 91 | * man('ls') 92 | * man('../man/multiexec.1') 93 | */ 94 | 95 | function man (path) { 96 | var page = /^\./.test(path) ? require('path').join(__dirname, path) : path; 97 | return require('child_process').execSync('man '+page, { stdio: 'inherit' }); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /man/multiexec.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "MULTIEXEC" "1" "April 2015" "@rstacruz" "" 5 | . 6 | .SH "NAME" 7 | \fBmultiexec\fR \- run 2 or more commands in parallel 8 | . 9 | .SH "SYNOPSIS" 10 | \fBmultiexec\fR \fIcommand1\fR [\fIcommand2\fR] [\fIcommandN\fR \.\.\.] 11 | . 12 | .SH "DESCRIPTION" 13 | It runs two or more commands in parallel\. 14 | . 15 | .P 16 | When one of the subprocesses exits with an error code, all the subprocesses will be terminated\. 17 | . 18 | .SH "OPTIONS" 19 | . 20 | .TP 21 | \fBcommandN\fR 22 | A command to be executed, including its arguments\. This command will be passed onto \fBsh \-c\fR, and so it will be shell\-separated as needed\. 23 | . 24 | .SH "EXAMPLE" 25 | If you dev environment has multiple processes running together, you can use \fBmultiexec\fR to run them both under one process\. In this example, we\'ll run Rails\'s development server with Guard\. 26 | . 27 | .IP "" 4 28 | . 29 | .nf 30 | 31 | multiexec "bundle exec rails s" "bundle exec guard" 32 | . 33 | .fi 34 | . 35 | .IP "" 0 36 | 37 | -------------------------------------------------------------------------------- /man/multiexec.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | multiexec(1) - run 2 or more commands in parallel 7 | 44 | 50 | 56 | 57 | 64 | 65 |
66 | 67 | 74 | 75 |
    76 |
  1. multiexec(1)
  2. 77 |
  3. 78 |
  4. multiexec(1)
  5. 79 |
80 | 81 |

NAME

82 |

83 | multiexec - run 2 or more commands in parallel 84 |

85 | 86 |

SYNOPSIS

87 | 88 |

multiexec command1 [command2] [commandN ...]

89 | 90 |

DESCRIPTION

91 | 92 |

It runs two or more commands in parallel.

93 | 94 |

When one of the subprocesses exits with an error code, all the subprocesses will be terminated.

95 | 96 |

OPTIONS

97 | 98 |
99 |
commandN
A command to be executed, including its arguments. This command will be passed onto sh -c, and so it will be shell-separated as needed.
100 |
101 | 102 | 103 |

EXAMPLE

104 | 105 |

If you dev environment has multiple processes running together, you can use multiexec to run them both under one process. In this example, we'll run Rails's development server with Guard.

106 | 107 |
multiexec "bundle exec rails s" "bundle exec guard"
108 | 
109 | 110 | 111 |
    112 |
  1. @rstacruz
  2. 113 |
  3. April 2015
  4. 114 |
  5. multiexec(1)
  6. 115 |
116 | 117 |
118 | 119 | 120 | -------------------------------------------------------------------------------- /man/multiexec.1.md: -------------------------------------------------------------------------------- 1 | # multiexec(1) -- run 2 or more commands in parallel 2 | 3 | ## SYNOPSIS 4 | 5 | `multiexec` [] [ ...] 6 | 7 | ## DESCRIPTION 8 | 9 | It runs two or more commands in parallel. 10 | 11 | When one of the subprocesses exits with an error code, all the subprocesses will be terminated. 12 | 13 | ## OPTIONS 14 | 15 | * `commandN` : 16 | A command to be executed, including its arguments. This command will be passed onto `sh -c`, and so it will be shell-separated as needed. 17 | 18 | ## EXAMPLE 19 | 20 | If you dev environment has multiple processes running together, you can use `multiexec` to run them both under one process. In this example, we'll run Rails's development server with Guard. 21 | 22 | ``` 23 | multiexec "bundle exec rails s" "bundle exec guard" 24 | ``` 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multiexec", 3 | "version": "0.0.3", 4 | "description": "Run multiple commands in parallel.", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepublish": "npm run build-man", 8 | "build-man": "if which ronn; then ronn man/*.md --html --roff --style=toc,80c --organization=\"@rstacruz\"; fi", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "directories": { 12 | "man": "man" 13 | }, 14 | "author": "Rico Sta. Cruz ", 15 | "bin": { 16 | "multiexec": "./bin/multiexec" 17 | }, 18 | "license": "MIT", 19 | "dependencies": { 20 | "split": "^0.3.3" 21 | }, 22 | "devDependencies": {}, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/rstacruz/multiexec.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/rstacruz/multiexec/issues" 29 | }, 30 | "homepage": "https://github.com/rstacruz/multiexec" 31 | } 32 | --------------------------------------------------------------------------------