UNPKG

@adp-psych/container-tools

Version:

Tools for using containers for psychology experiments

141 lines (130 loc) 4.19 kB
/* * Copyright (C) 2021 Anthony Di Pietro * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ /** * @file Executes a system command and sends stdout/stderr to stdout/stderr. * * @module module:console-exec * @author Anthony Di Pietro <anthony.dipietro@research.uwa.edu.au> * @copyright © 2021 Anthony Di Pietro * @license AGPL-3.0-or-later */ /* eslint-disable promise/avoid-new -- Promisifying Node.js streams. */ const { 'exec': callbackExec, 'execFile': callbackExecFile, spawn, } = require('node:child_process'); const {promisify} = require('node:util'); const exec = promisify(callbackExec); const execFile = promisify(callbackExecFile); /** * Tests whether a string is `undefined`, `null`, or empty. * * @static * @access protected * @param {String} x - The string. * @returns {Boolean} Whether the string is `undefined`, `null`, or empty. * @example * nilOrEmpty(''); // true */ const nilOrEmpty = (x) => typeof x === 'undefined' || x === null || x === ''; /** * Strips a newline from the end of a string. * * If thes string does not end in a newline, this does nothing and * simply returns the string. * * @static * @access protected * @param {String} x - The string. * @returns {String} The string with a newline stripped from the end. * @example * stripNewline('abc\n'); // 'abc' */ const stripNewline = (x) => x.replace(/\n$/u, ''); /** * Runs a command via the system shell and sends stdout/stderr to stdout/stderr. * * This uses * [`child_process.exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback). * * @static * @param {String} command - The command. * @example * consoleExec('ls -al'); // Executes the system command 'ls -al'. */ const consoleExec = async (command) => { const {stdout, stderr} = await exec(command); if (!nilOrEmpty(stdout)) { console.log(stripNewline(stdout)); } if (!nilOrEmpty(stderr)) { console.error(stripNewline(stderr)); } }; /** * Runs an executable file and sends stdout/stderr to stdout/stderr. * * This uses * [`child_process.execFile()`](https://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback). * * @static * @param {String} file - The executable file. * @param {Array.<String>} args - The command arguments. * @example * consoleExecFile(['ls', '-al']); // Executes the system command 'ls -al'. */ const consoleExecFile = async (file, args) => { const {stdout, stderr} = await execFile(file, args); if (!nilOrEmpty(stdout)) { console.log(stripNewline(stdout)); } if (!nilOrEmpty(stderr)) { console.error(stripNewline(stderr)); } }; /** * Runs an executable file and sends stdout/stderr to stdout/stderr. * * This uses * [`child_process.spawn()`](https://nodejs.org/api/all.html#child_process_child_process_spawn_command_args_options). * * @async * @static * @param {String} file - The executable file. * @param {Array.<String>} args - The command arguments. * @returns {Number} The exit code of the subprocess. * @example * consoleSpawn(['ls', '-al']); // Executes the system command 'ls -al'. */ const consoleSpawn = (file, args) => new Promise((resolve, reject) => { const subprocess = spawn(file, args); subprocess.stdout.on('data', (data) => { process.stdout.write(data); }); subprocess.stderr.on('data', (data) => { process.stderr.write(data); }); subprocess.on('close', (code) => ( code === 0 ? resolve(code) : reject(code) )); }); module.exports = { consoleExec, consoleExecFile, consoleSpawn, };