node.js child_process spawn ignoring equal signs - node.js

I'm trying to launch a cordova command with a target device. I've tested the command and it works but when I try to generate it with my code, it ignores the equal sign and therefore won't run. This code does work just not with the addition of "--target='iPhone-7-Plus"
return new Promise((resolve, reject) => {
const executable = "ionic";
const arguments = [
"cordova",
buildOnly ? "build" : "run",
platform,
"--no-interactive",
"--verbose",
"--target='iPhone-7-Plus'"
].concat(releaseDev === "release" ? ["--prod", "--release"] : []);
console.log(executable, arguments.join(" "));
const child = spawn(executable, arguments, {
stdio: "inherit"
});
child.on("close", () => resolve());
child.on("error", err => reject(err));
});
What am I doing wrong here? Why would it be ignoring my equal sign only but the rest of the command gets added?
If I run cordova run ios --target='iPhone-7-Plus' the command will execute and launch the 7+ simulator without issues.

When spawning I had to add shell: true in order to use the default shell for my os. The shell that spawn was using would strip special characters.
const child = spawn(executable, arguments, {
stdio: "inherit",
shell: true
});

Related

child_process.execSync(start "" "example.exe") freezing/locking console with executables have params option

When I try to execute an executable that have an option to have parameters. It will freeze the nodejs output and input until the executable is closed. Executables that do not need params will just run, and the nodejs console will not freeze/lock input nor output.
Example with param: test.exe -thisisaparam. Example without Params: test.exe.
Here is my code below. (Its a cli)
const cp = require('child_process');
let start = async function (start) {
let command = `start "" ${start}`;
cp.execSync(command);
console.log("Returning to menu in 10 seconds...")
setTimeout(() => {
run()
}, 10000);
};
Here is how I call the function.
async function startTest() {
await start("C:\users\user\downloads\test_param.exe")
}
Thanks, Kiefer.
Any command you run using execSync will be synchronous meaning it will wait for the command to exit and then returns the output.
If you don't need the output of the command and want to just start and detach you should use spawn with unref().
example:
const scriptPath = "C:\users\user\downloads\test_param.exe"
cp.spawn('start', [scriptPath], {detached: true, stdio: 'ignore'}).unref()

How can I handle an input prompt with node when executing a command via child_process?

For context, I'm on a Mac and I'm trying to script a 1Password CLI signin via their command-line tool. I'm trying to programmatically signing using a command that looks like:
op signin <signinaddress> <emailaddress> <secretkey> --output=raw
and I've tried with/without the --output=raw argument, but every time I simply get an error that looks like
[LOG] 2019/06/04 00:57:45 (ERROR) operation not supported on socket
child process exited with code 1
My initial hunch was that it had something to do with the command executions prompt displaying this special key character in the following image:
The relevant code is written in TypeScript and looks like this:
import { spawn } from 'child_process'
// ends up being `op signin <signinaddress> <emailaddress> <secretkey>`
const op = spawn(opExecutable, args);
let result: string | null = null
op.on('message', (message, sendHandle) => {
console.log('message', message, sendHandle)
});
op.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
if (data && typeof data.toString === 'function') {
result = data.toString()
}
});
op.on('close', (code, ...args) => {
console.log(`child process exited with code ${code}`, args);
});
Eventually, I'd like to run on all platforms and be able pass in stdin for the master password required to sign in, but I'm trying to figure out why my node app is crashing first :)
Apparently I was pretty close to a solution by using spawn, but I needed to specify configuration for stdio. Here's an example snippet of how I used spawn that worked for me:
const proc = spawn(
cmd, // the command you want to run, in my case `op`
args, // arguments you want to use with the above cmd `signin`, etc.
{
stdio: [
'inherit', // stdin: changed from the default `pipe`
'pipe', // stdout
'inherit' // stderr: changed from the default `pipe`
]
});

How to pass command line arguments to NodeJS launched from an executable script

How to set what would otherwise be command-line arguments to node for a NodeJS process run from a launcher script? (The sh/CMD scripts npm places into node_modules/.bin.)
Plenty of NodeJS libraries / frameworks come with their own runner script, e.g. zeit/micro or moleculer that's usually executed from a npm script. This presents a problem in development, since in my case I want to do the equivalent of:
node --inspect -r ts-node/register -r dotenv-safe/config src/index.ts
(Except, of course, that does nothing since index.ts just exports something for the runner to pick up.)
Is there some "clean", preferably generic (i.e. not specific to a given framework's runner exposing those command line params) way that I'm missing to do this, ideally one that works as a npm script? The only thing that seems like it would work would be for e.g. micro:
node-dev -r ts-node/register ./node_modules/micro-dev/bin/micro-dev.js ./src/index.ts
which is kind of a mouthful from the Redundant Department of Redundancy Department and seems to obviate the point of having those launcher scripts. (It also won't work if the runner spawns other Node processes, but that's not a problem I'm actually having.) I'd like to not have to duplicate what the launcher scripts are already doing. I'm also aware of npx having --node-arg but npx is a whole another can of worms. (On Windows it's five seconds of startup time and one spurious error message just to run a script I already have installed; it also won't find an already installed package if it can't find its .cmd launcher script, e.g. when using Docker to run the dev environment. In short I'd rather not use npx for this.)
To clear up the confusion that seems to crop up in the comments: I want to override the command line parameters that affect the behaviour of the NodeJS runtime itself executing the runner script, not pass parameters to the script itself or to my code. That is, the options listed here: https://nodejs.org/api/cli.html
One option is to write a little wrapper script that uses the current process execPath to run child_process.execFile.
So the sample here is to be able to do
node --expose-http2 --zero-fill-buffers -r ./some-module.js ./test.js
but not actually write that out, instead have wrap.js inject the args:
node ./wrap.js ./test.js
I tested running this via npm in a package.json, and it works fine. I tested that it was working by having some-module.js stick a value on the global object, and then logging it in test.js.
Files involved:
wrap.js
const child_process = require('child_process');
const nodeArgs = ['--expose-http2', '--zero-fill-buffers', '-r', './some-module.js'];
const runTarget = process.argv[2];
console.log('going to wrap', runTarget, 'with', nodeArgs);
const finalArgs = nodeArgs.concat(runTarget).concat(process.argv.slice(2));
const child = child_process.execFile(
process.execPath,
finalArgs,
{
env: process.env,
cwd: process.cwd(),
stdio: 'inherit'
}, (e, stdout, stderr) => {
console.log('process completed');
if (e) {
process.emit('uncaughtException', e);
}
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
and
some-module.js
global.testval = 2;
and
test.js
console.log('hi guys, did the wrap work?', global.testval)
EDIT: So upon further thought, this solution really only satisfies wrapping the initial runner. But most tools, such as mocha re-spawn a sub process which would then lose this effect. To really get the job done, you can proxy each of the child process calls and somewhat enforce that calls to spawn and such also include your args.
I rewrote the code to reflect this. Here's a new setup:
package.json
{
"scripts": {
"test": "node -r ./ensure-wrapped.js node_modules/mocha/$(npm view mocha bin.mocha) ./test.js"
},
"dependencies": {
"mocha": "^5.1.0"
}
}
ensure-wrapped.js
const child_process = require('child_process');
// up here we can require code or do whatever we want;
global.testvalue = 'hi there'
const customParams = ['--zero-fill-buffers'];
// the code below injects itself into any child process's spawn/fork/exec calls
// so that it propogates
const matchNodeRe = /((:?\s|^|\/)node(:?(:?\.exe)|(:?\.js)|(:?\s+)|$))/;
const ensureWrappedLocation = __filename;
const injectArgsAndAddToParamsIfPathMatchesNode = (cmd, args, params) => {
params.unshift(...customParams);
params.unshift(args);
if (!Array.isArray(args)) { // all child_proc functions do [] optionally, then other params
args = []
params.unshift(args);
}
if (!matchNodeRe.test(cmd)) {
return params;
}
args.unshift(ensureWrappedLocation);
args.unshift('-r');
return params;
}
child_process._exec = child_process.exec;
child_process.exec = (cmd, ...params) => {
// replace node.js node.exe or /path/to/node to inject -r ensure-wrapped.js ...args..
// leaves alone exec if it isn't calling node
cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' ');
return child_process._exec(cmd, ...params)
}
child_process._execFile = child_process.execFile;
child_process.execFile = (path, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params);
return child_process._execFile(path, ...params)
}
child_process._execFileSync = child_process.execFileSync;
child_process.execFileSync = (path, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params);
return child_process._execFileSync(path, ...params);
}
child_process._execSync = child_process.execSync;
child_process.execSync = (cmd, ...params) => {
cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' ');
return child_process._exec(bin, ...args)
}
child_process._fork = child_process.fork;
child_process.fork = (module, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(process.execPath, args, params);
return child_process._fork(module, ...params);
}
child_process._spawn = child_process.spawn;
child_process.spawn = (cmd, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params);
return child_process._spawn(cmd, ...params)
}
child_process._spawnSync = child_process.spawnSync;
child_process.spawnSync = (cmd, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params);
return child_process._spawnSync(cmd, ...params);
}
test.js
describe('test', () => {
it('should have the global value pulled in by some-module.js', (done) => {
if (global.testvalue !== 'hi there') {
done(new Error('test value was not globally set'))
}
return done();
})
})
Please never put code like this into a node module that's published. modifying the global library functions is pretty bad.
Everything passed in the command line AFTER your nodejs application is parsed into an array called process.argv. So...
node myapp.js foo bar hello 5000
In your nodejs code...
const args = process.argv;
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
console.log(args[3]);
would yield...
foo
bar
hello
5000
I didnt get clear scenario of your problem,but as your question title ,we can execute the any cmd command from nodejs using npm libraries like:
import Promise from 'bluebird'
import cmd from 'node-cmd'
const getAsync = Promise.promisify(cmd.get, { multiArgs: true, context: cmd })
getAsync('node -v').then(data => {
console.log('cmd data', data)
}).catch(err => {
console.log('cmd err', err)
})

Send the same bash process multiple commands

I have the following code:
const cp = require('child_process');
function spawnInstance () {
const c = cp.spawn('bash');
return command => {
return new Promise((resolve, reject) => {
c.stdout.on('data', d => resolve(String(d || 'empty stdout.\n')));
c.stderr.once('data', d => reject(String(d || 'empty stderr.\n')));
c.stdin.write(`echo "${command}" | bash;`);
c.stdin.write('\n');
});
};
}
(async () => {
const bash = spawnInstance();
console.log(await bash('ls'));
console.log(await bash('cd node_modules'));
console.log(await bash('ls'));
})()
.catch(e =>
console.error(e)
);
What I want to do is to reuse the same bash process, and get the stdout for each command that I run. Is there a way to do this, or do I need to start a new bash process for each command I run?
The problem is my code gets stuck on the cd node_modules command.
I don't think you can keep a bash process alive after a bash call and then re-enter the process with some new call. You can however run multiple bash commands after each other in the same process, e.g. by separating them with semicolons:
console.log(await bash('cd node_modules; ls'));
This article states that now there is an NPM package that allows for a persistent bash process.
The github url:
https://github.com/bitsofinfo/stateful-process-command-proxy
Quote from the him homepage:
This node module can be used for proxying long-lived bash process, windows console etc. It works and has been tested on both linux, os-x and windows hosts running the latest version of node.

How can I parse a string into appropriate arguments for child_process.spawn?

I want to be able to take a command string, for example:
some/script --option="Quoted Option" -d --another-option 'Quoted Argument'
And parse it into something that I can send to child_process.spawn:
spawn("some/script", ["--option=\"Quoted Option\"", "-d", "--another-option", "Quoted Argument"])
All of the parsing libraries I've found (e.g. minimist, etc.) do too much here by parsing it into some kind of options object, etc. I basically want the equivalent of whatever Node does to create process.argv in the first place.
This seems like a frustrating hole in the native APIs since exec takes a string, but doesn't execute as safely as spawn. Right now I'm hacking around this by using:
spawn("/bin/sh", ["-c", commandString])
However, I don't want this to be tied to UNIX so strongly (ideally it'd work on Windows too). Halp?
Standard Method (no library)
You don't have to parse the command string into arguments, there's an option on child_process.spawn named shell.
options.shell
If true, runs command inside of a shell.
Uses /bin/sh on UNIX, and cmd.exe on Windows.
Example:
let command = `some_script --option="Quoted Option" -d --another-option 'Quoted Argument'`
let process = child_process.spawn(command, [], { shell: true }) // use `shell` option
process.stdout.on('data', (data) => {
console.log(data)
})
process.stderr.on('data', (data) => {
console.log(data)
})
process.on('close', (code) => {
console.log(code)
})
The minimist-string package might be just what you're looking for.
Here's some sample code that parses your sample string -
const ms = require('minimist-string')
const sampleString = 'some/script --option="Quoted Option" -d --another-option \'Quoted Argument\'';
const args = ms(sampleString);
console.dir(args)
This piece of code outputs this -
{
_: [ 'some/script' ],
option: 'Quoted Option',
d: true,
'another-option': 'Quoted Argument'
}

Resources