I am trying to use node to execute a bash command that takes a nested json object as an argument. I am finding that nested json does not work.
For example trying to convert this bash command. Notice awscloudformation is a nested object.
#!/bin/bash
set -e
IFS='|'
AWSCLOUDFORMATIONCONFIG="{\
\"configLevel\":\"project\",\
\"useProfile\":true,\
\"profileName\":\"testing\"\
}"
PROVIDERS="{\
\"awscloudformation\":$AWSCLOUDFORMATIONCONFIG\
}"
amplify init \
--providers $PROVIDERS \
Here is the node script that does not work as expected. Providers seems to be ignored and is not overriding the defaults like the above bash script.
const arguments = [
'--providers',
`{"awscloudformation":{"configLevel":"project","useProfile":true,"profileName":"testing"}}`,
];
const opts = { stdio: 'inherit', shell: true };
require('child_process').spawn('amplify init', getArgs(), opts);
Would exec be better suited here? It seems I would need to include a variable similar to how $AWSCLOUDFORMATIONCONFIG is being used in the bash script.
It is interesting to note that other arguments that are not nested seem to work fine. For example this amplify argument works fine:
'--amplify',
'{"projectName":"checking987","envName":"dev","defaultEditor":"code"}',
It's better to use shell: false here :
const arguments = [
'--providers',
`{"awscloudformation":{"configLevel":"project","useProfile":true,"profileName":"testing"}}`,
];
const opts = { stdio: 'inherit', shell: false };
require('child_process').spawn("amplify", ["init", ...arguments], opts);
In shell mode, double quotes are removed.
Adapted shell mode version which works :
const arguments = [
'--providers',
`{\\"awscloudformation\\":{\\"configLevel\\":\\"project\\",\\"useProfile\\":true,\\"profileName\\":\\"testing\\"}}`,
];
const opts = { stdio: 'inherit', shell: true };
require('child_process').spawn("amplify init", arguments, opts);
Related
If I have this script in node, saved as example.js:
const childProcess = require('child_process');
childProcess.spawn('vim', ['-u NONE', 'test.txt'], { stdio: 'inherit' });
I would expect that node example.js would be (roughly) equivalent to calling:
vim -u NONE test.txt
However, when I execute the script I get:
$ node example.js
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Dec 26 2020 07:37:17)
Garbage after option argument: "-u NONE"
More info with: "vim -h"
Running vim -u NONE example.txt directly works just fine.
Am I misunderstanding / misusing spawn somehow?
Edit:
This, without the -u NONE flag, works just fine:
childProcess.spawn('vim', ['test.txt'], { stdio: 'inherit' });
It's adding -u NONE that for some reason vim doesn't like.
This should work
const cp = spawn('vim', ['test.txt'], {stdio: ['inherit']})
cp.stdout.pipe(process.stdout)
Spawn open a child_process which is attached to the const cp(in this ex), this method stream its output (https://nodejs.org/dist/latest-v14.x/docs/api/child_process.html), so we need to consume it. We pipe the cp.stdout to the parent_process process.stdout. That open a new tty within: the child_process.stdout
We can make it more straightforward by setting the stdio childProcess.output option to 'inherit' and(optional) forwarding the childProcess.stderr to its own childProcess.stdout (which is already inherit by the motherProcess, so will be output automatically).
This should output the same as previously
const cp = spawn(
'vim', ['test.txt'],
{stdio: ['inherit', 'inherit', process.stdout]}
)
To run precisely the command vim -u NONE file.ext. The first argument is the executable path and the second is an array containing the flags to pass to the command. Inside this array each single element(separate with space) of the flags must be an element of the array. So in this case that should work
const cp = spawn(
'vim', ['-u', 'NONE', 'test.txt'],
{stdio: ['inherit', 'inherit', process.stdout]}
)
You could use it like this:
const { exec } = require("child-process");
exec('vim -u NONE example.txt', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.log(stdout);
});
When I call the following in the command line it works like a charm:
inkscape --with-gui --batch-process --export-filename=- \
--actions="select-all;ObjectToPath" \
/full/path/to/example.svg
But when I open Node.js and make the same call in a subprocess like so:
const cp = require("child_process");
var child = cp.spawn(
"/usr/bin/inkscape",
[
"--with-gui",
"--batch-process",
"--export-filename=-",
'--actions="select-all;ObjectToPath"',
"/full/path/to/example.svg",
],
{
cwd: process.cwd(),
detached: true,
stdio: "inherit",
}
);
I get the following error:
Unable to find: "select-all
verbs_action: Invalid verb: "select-all
Unable to find: ObjectToPath"
verbs_action: Invalid verb: ObjectToPath"
and the file is returned (printed to stdout) unchanged. Any Idea why the verbs are not found when running Inkscape as a subprocess but not calling it directly from the terminal? I get this same error on ubuntu (20.04) and OSX using the latest Inkscape (1.0.1+r73).
When you use cp.spawn with an array of arguments, you don't need to internally quote "select-all;ObjectToPath" like you would with a shell. (In a shell, the quotes prevent the shell from tokenizing the command line into two lines. Due to the same mechanism - or lack thereof - attempting to use shell variables such as $$ or environment variables such as $PATH would fail when you use cp.spawn, since there's nothing to parse that.)
I would imagine
const cp = require("child_process");
var child = cp.spawn(
"/usr/bin/inkscape",
[
"--with-gui",
"--batch-process",
"--export-filename=-",
"--actions=select-all;ObjectToPath",
"/full/path/to/example.svg",
],
{
cwd: process.cwd(),
detached: true,
stdio: "inherit",
},
);
would do the trick for you.
I would like to run 2 commands with nodeJS in the same shell. However, the documentation seems that it can only run a single command child_process.spawn(command[, args][, options])
The reason 2 commands have to be executed in the same shell is because the first script would write into the environment and the second script will read from it.
This is an example of the script but I would like both ps and grep to be in the same shell.
const { spawn } = require('child_process');
const ps = spawn('ps', ['ax'], { shell: true });
const grep = spawn('grep', ['ssh'],{ shell: true });
Try
ps.stdout.pipe(grep.stdin);
I'm spawning a process with child_process.spawn(). How can I view the exact command line that is executed (including all command-line parameters passed into the spawn function)?
Here's my example that isn't working:
var s = require('child_process');
var p = s.spawn('ffmpeg', ['-probesize 1024', '-i "rtsp://192.168.1.10:554/11"', 'test.h264']);
When I capture the stderr, I get a message "Unrecognized option 'probesize 1024'. However, if I run ffmpeg from the command line, it works fine. So I need to see exactly how the command-line options are being mangled before being sent to FFMPEG in order to debug this.
Thanks
Try
var p = s.spawn('ffmpeg', [
'-probesize',
'1024',
'-i',
'rtsp://192.168.1.10:554/11',
'test.h264'
]);
Command line arguments that have a space between them but are coherent, still need to be separated. So there's needs to be a space between -probesize and 1024
Update
If you would like to keep the coherent arguments together, add shell: true to the options object:
var p = s.spawn('ffmpeg',
[
'-probesize 1024',
'-i "rtsp://192.168.1.10:554/11"',
'test.h264'
],
{
shell: true
}
);
Old question I know...but one idea I had, which might answer the OP's question
const cp = require('child_process');
var sp = cp.spawn( 'cmd', [ '/C' , 'echo', 'ffmpeg',
'-probesize',
'1024',
'-i',
'rtsp://192.168.1.10:554/11',
'test.h264'
]);
sp.stdout.on( "data" , (d) => {
console.log( `${d}`);
});
which will echo the cmd to the console...
ffmpeg -probesize 1024 -i rtsp://192.168.1.10:554/11 test.h264
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'
}