'Invalid Verb' error only when Calling Inkscape as a subprocess - node.js

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.

Related

Nested json argument in node child process bash script

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);

nodejs spawn not constructing valid vim command

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);
});

Get full command-line string from child_process.spawn

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

Error: spawn ENOENT on Windows

I'm on node v4.4.0 and on Windows 10. I'm using bunyan to log my node application.
try {
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var through = require('through');
} catch (err) {
throw err;
}
var prettyStream = function () {
// get the binary directory of bunyan
var bin = path.resolve(path.dirname(require.resolve('bunyan')), '..', 'bin', 'bunyan');
console.log(bin); // this outputs C:\www\nodeapp\src\node_modules\bunyan\bin\bunyan, the file does exist
var stream = through(function write(data) {
this.queue(data);
}, function end() {
this.queue(null);
});
// check if bin var is not empty and that the directory exists
if (bin && fs.existsSync(bin)) {
var formatter = spawn(bin, ['-o', 'short'], {
stdio: [null, process.stdout, process.stderr]
});
// stream.pipe(formatter.stdin); // <- did this to debug
}
stream.pipe(process.stdout); // <- did this to debug
return stream;
}
The logging spits out in the console due to the fact I used stream.pipe(process.stdout);, i did this to debug the rest of the function.
I however receive the error:
Error: spawn C:\www\nodeapp\src\node_modules\bunyan\bin\bunyan ENOENT
at exports._errnoException (util.js:870:11)
at Process.ChildProcess._handle.onexit (internal/child_process.js:178:32)
at onErrorNT (internal/child_process.js:344:16)
at nextTickCallbackWith2Args (node.js:442:9)
at process._tickCallback (node.js:356:17)
at Function.Module.runMain (module.js:443:11)
at startup (node.js:139:18)
at node.js:968:3
I'm guessing this is a Windows error. Anyone have any ideas?
Use {shell: true} in the options of spawn
I was hit with this problem recently so decided to add my findings here. I finally found the simplest solution in the Node.js documentation. It explains that:
child_process.exec() runs with shell
child_process.execFile() runs without shell
child_process.spawn() runs without shell (by default)
This is actually why the exec and spawn behave differently. So to get all the shell commands and any executable files available in spawn, like in your regular shell, it's enough to run:
const { spawn } = require('child_process')
const myChildProc = spawn('my-command', ['my', 'args'], {shell: true})
or to have a universal statement for different operating systems you can use
const myChildProc = spawn('my-command', ['my', 'args'], {shell: process.platform == 'win32'})
Side notes:
It migh make sense to use such a universal statement even if one primairly uses a non-Windows system in order to achieve full interoperability
For full consistence of the Node.js child_process commands it would be helpful to have spawn (with shell) and spawnFile (without shell) to reflect exec and execFile and avoid this kind of confusions.
I got it. On Windows bunyan isn't recognized in the console as a program but as a command. So to invoke it the use of cmd was needed. I also had to install bunyan globally so that the console could access it.
if (!/^win/.test(process.platform)) { // linux
var sp = spawn('bunyan', ['-o', 'short'], {
stdio: [null, process.stdout, process.stderr]
});
} else { // windows
var sp = spawn('cmd', ['/s', '/c', 'bunyan', '-o', 'short'], {
stdio: [null, process.stdout, process.stderr]
});
}
I solved same problem using cross-spawn. It allows me to spawn command on both windows and mac os as one common command.
I think you'll find that it simply can't find 'bunyun', but if you appended '.exe' it would work. Without using the shell, it is looking for an exact filename match to run the file itself.
When you use the shell option, it goes through matching executable extensions and finds a match that way. So, you can save some overhead by just appended the executable extension of your binary.
I was having this same problem when trying to execute a program in the current working directory in Windows. I solved it by passing the options { shell: true, cwd: __dirname } in the spawn() call. Then everything worked, with every argument passed as an array (not attached to the program name being run).
I think, the path of bin or something could be wrong. ENOENT = [E]rror [NO] [ENT]ry

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