Created
May 17, 2018 05:52
-
-
Save swcho/11c629c536e98e44e1047ee386f642e3 to your computer and use it in GitHub Desktop.
Please Use TypeScript For Shell Programming
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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