Skip to content

Instantly share code, notes, and snippets.

@swcho
Created May 17, 2018 05:52
Show Gist options
  • Save swcho/11c629c536e98e44e1047ee386f642e3 to your computer and use it in GitHub Desktop.
Save swcho/11c629c536e98e44e1047ee386f642e3 to your computer and use it in GitHub Desktop.
Please Use TypeScript For Shell Programming
import cp = require('child_process');
import chalk from 'chalk';
import readline = require('readline');
import { SpawnOptions } from 'child_process';
let sh,
cmd,
defSpawnOptions = { stdio: 'inherit' };
/**
* @summary Get shell program meta for current platform
* @private
* @returns {Object}
*/
function getShell() {
if (process.platform === 'win32') {
return { cmd: 'cmd', arg: '/C' };
} else {
return { cmd: 'sh', arg: '-c' };
}
}
/**
* Callback is called with the output when the process terminates. Output is
* available when true is passed as options argument or stdio: null set
* within given options.
*
* @summary Execute shell command forwarding all stdio
* @param {String|Array} command
* @param {Object|TRUE} [options] spawn() options or TRUE to set stdio: null
* @param {Function} [callback]
* @returns {ChildProcess}
*/
function execSh(command, options, callback) {
if (Array.isArray(command)) {
command = command.join(';');
}
if (typeof options === 'function') {
callback = options;
options = defSpawnOptions;
} else {
options = options || {};
options = { ...defSpawnOptions, ...options };
callback = callback || function () {};
}
let child: cp.ChildProcess;
let stdout = '';
let stderr = '';
const shell = getShell();
try {
child = cp.spawn(shell.cmd, [shell.arg, command], options);
} catch (e) {
callback(e, stdout, stderr);
return;
}
if (child.stdout) {
child.stdout.on('data', (data) => {
stdout += data;
});
readline.createInterface({
input: child.stdout,
terminal: false,
}).on('line', console.log);
}
if (child.stderr) {
child.stderr.on('data', (data) => {
stderr += data;
});
readline.createInterface({
input: child.stderr,
terminal: false,
}).on('line', line => console.log(chalk.red(line)));
}
child.on('close', function (code) {
if (code) {
const e = new Error('Shell command exit with non zero code: ' + code);
e['code'] = code;
callback(e, stdout, stderr);
} else {
callback(null, stdout, stderr);
}
});
return child;
}
interface ExecOptions extends SpawnOptions {
re?: RegExp;
}
export function exec(cmd, options: ExecOptions = {}): Promise<exec.Ret> {
const {
re,
...spawnOptions,
} = options;
console.log(chalk.green(cmd));
if (re) {
spawnOptions.stdio = [process.stdin, null, null];
}
return new Promise((resolve, reject) => {
execSh(cmd, spawnOptions, (err, stdout, stderr) => {
if (err) {
reject(err);
} else {
if (re) {
const match = re.exec(stdout);
if (match) {
resolve({
stdout,
stderr,
match,
});
} else {
reject('NOT MATCHAED');
}
} else {
resolve({
stdout,
stderr,
});
}
}
});
});
}
namespace exec {
export interface Ret {
stdout: string;
stderr: string;
match?: RegExpExecArray;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment