nodejs spawn not constructing valid vim command - node.js

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

Related

'Invalid Verb' error only when Calling Inkscape as a subprocess

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.

Open terminal and launch commands

In my electron/reactjs app, i'm trying to open a terminal and launch somes commands.
My code looks like this :
const terminal = 'x-terminal-emulator';
const { spawn } = require('child_process');
spawn(terminal);
My terminal opens but i don't know how to launch commands in this terminal like 'cd /my/custom/path && ls'
Can someone help me please ? :)
Node.js child_process.spawn command have an option to specify the shell you want to use.
So I would use the opposite logic and launch directly the command within a particular shell (for exemple bash):
const { spawn } = require('child_process');
const terminal = '/bin/bash';
let cmd = 'echo $SHELL';
spawn(cmd, { shell: terminal })
.stdout.on('data', (data) => {
console.log(`stdout: ${data}`); //-> stdout: /bin/bash
});

How do use nodejs childprocess exec to run the unix diff command?

I need to use node to generate diffs of some files. I have tried the diff npm package, and while that works great it is much less performant than the version of diff you will find in /usr/bin/.
When I try to use exec to run the diff command it always errors.
var childProcess = require('child_process');
var cmd = "diff /path/to/file1.txt /path/to/file2.txt";
childProcess.exec(cmd, (error, stdout, stderr) => {
if(error) {
console.log(error);
} else {
console.log(stdout);
}
});
The output:
{ [Error: Command failed: /bin/sh -c diff /path/to/file1.txt /path/to/file2.txt
]
killed: false,
code: 1,
signal: null,
cmd: '/bin/sh -c diff /path/to/file1.txt /path/to/file2.txt' }
If I run the command on the command line myself it works fine.
I've tried running /usr/bin/diff instead of just diff.
I've tried various different forms of quoting things.
Every other command I have tried, using the exact same files, has worked. cat, wc, etc.
Any thoughts?
Welp, I am answering my own question.
The diff command returns a failure exit code if it finds a difference. Therefore the console.log(stdout) call was never being reached.
If I ignore the error, everything works.
var childProcess = require('child_process');
var cmd = "diff /path/to/file1.txt /path/to/file2.txt";
childProcess.exec(cmd, (error, stdout, stderr) => {
console.log(stdout);
});
works like a charm.

Use child_process.execSync but keep output in console

I'd like to use the execSync method which was added in NodeJS 0.12 but still have the output in the console window from which i ran the Node script.
E.g. if I run a NodeJS script which has the following line I'd like to see the full output of the rsync command "live" inside the console:
require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"');
I understand that execSync returns the ouput of the command and that I could print that to the console after execution but this way I don't have "live" output...
You can pass the parent´s stdio to the child process if that´s what you want:
require('child_process').execSync(
'rsync -avAXz --info=progress2 "/src" "/dest"',
{stdio: 'inherit'}
);
You can simply use .toString().
var result = require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"').toString();
console.log(result);
Edit: Looking back on this, I've realised that it doesn't actually answer the specific question because it doesn't show the output to you 'live' — only once the command has finished running.
However, I'm leaving this answer here because I know quite a few people come across this question just looking for how to print the result of the command after execution.
Unless you redirect stdout and stderr as the accepted answer suggests, this is not possible with execSync or spawnSync. Without redirecting stdout and stderr those commands only return stdout and stderr when the command is completed.
To do this without redirecting stdout and stderr, you are going to need to use spawn to do this but it's pretty straight forward:
var spawn = require('child_process').spawn;
//kick off process of listing files
var child = spawn('ls', ['-l', '/']);
//spit stdout to screen
child.stdout.on('data', function (data) { process.stdout.write(data.toString()); });
//spit stderr to screen
child.stderr.on('data', function (data) { process.stdout.write(data.toString()); });
child.on('close', function (code) {
console.log("Finished with code " + code);
});
I used an ls command that recursively lists files so that you can test it quickly. Spawn takes as first argument the executable name you are trying to run and as it's second argument it takes an array of strings representing each parameter you want to pass to that executable.
However, if you are set on using execSync and can't redirect stdout or stderr for some reason, you can open up another terminal like xterm and pass it a command like so:
var execSync = require('child_process').execSync;
execSync("xterm -title RecursiveFileListing -e ls -latkR /");
This will allow you to see what your command is doing in the new terminal but still have the synchronous call.
Simply:
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
console.log(`Status Code: ${error.status} with '${error.message}'`;
}
Ref: https://stackoverflow.com/a/43077917/104085
// nodejs
var execSync = require('child_process').execSync;
// typescript
const { execSync } = require("child_process");
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
error.status; // 0 : successful exit, but here in exception it has to be greater than 0
error.message; // Holds the message you typically want.
error.stderr; // Holds the stderr output. Use `.toString()`.
error.stdout; // Holds the stdout output. Use `.toString()`.
}
When command runs successful:
Add {"encoding": "utf8"} in options.
execSync(`pwd`, {
encoding: "utf8"
})

Running a shell command from Node.js without buffering output

I'm trying to launch a shell command from Node.js, without redirecting that command's input and output -- just like shelling out to a command using a shell script, or using Ruby's system command. If the child process wants to write to STDOUT, I want that to go straight to the console (or get redirected, if my Node app's output was redirected).
Node doesn't seem to have any straightforward way to do this. It looks like the only way to run another process is with child_process, which always redirects the child process's input and output to pipes. I can write code to accept data from those pipes and write it to my process's STDOUT and STDERR, but if I do that, the APIs force me to sacrifice some flexibility.
I want two features:
Shell syntax. I want to be able to pipe output between commands, or run Windows batch files.
Unlimited output. If I'm shelling out to a compiler and it wants to generate megabytes of compiler warnings, I want them all to scroll across the screen (until the user gets sick of it and hits Ctrl+C).
It looks like Node wants to force me choose between those two features.
If I want an unlimited amount of output, I can use child_process.spawn and then do child.stdout.on('data', function(data) { process.stdout.write(data); }); and the same thing for stderr, and it'll happily pipe data until the cows come home. Unfortunately, spawn doesn't support shell syntax.
If I want shell syntax, I can use child_process.exec. But exec insists on buffering the child process's STDOUT and STDERR for me and giving them to me all at the end, and it limits the size of those buffers (configurable, 200K by default). I can still hook the on('data') events, if I want to see the output as it's generated, but exec will still add the data to its buffers too. When the amount of data exceeds the predefined buffer size, exec will terminate the child process.
(There's also child_process.execFile, which is the worst of both worlds from a flexibility standpoint: no shell syntax, but you still have to cap the amount of output you expect.)
Am I missing something? Is there any way to just shell out to a child process in Node, and not redirect its input and output? Something that supports shell syntax and doesn't crap out after a predefined amount of output, just like is available in shell scripts, Ruby, etc.?
You can inherit stdin/out/error streams via spawn argument so you don't need to pipe them manually:
var spawn = require('child_process').spawn;
spawn('ls', [], { stdio: 'inherit' });
Use shell for shell syntax - for bash it's -c parameter to read script from string:
var spawn = require('child_process').spawn;
var shellSyntaxCommand = 'ls -l | grep test | wc -c';
spawn('sh', ['-c', shellSyntaxCommand], { stdio: 'inherit' });
To summarise:
var spawn = require('child_process').spawn;
function shspawn(command) {
spawn('sh', ['-c', command], { stdio: 'inherit' });
}
shspawn('ls -l | grep test | wc -c');
You can replace exec by spawn and use the shell syntax simply with:
const {spawn} = require ('child_process');
const cmd = 'ls -l | grep test | wc -c';
const p = spawn (cmd, [], {shell: true});
p.stdout.on ('data', (data) => {
console.log (data.toString ());
});
The magic is just {shell: true}.
I haven't used it, but I've seen this library: https://github.com/polotek/procstreams
It you'd do this. The .out() automatically pipes to the process's stdin/out.
var $p = require('procstreams');
$p('cat lines.txt').pipe('wc -l').out();
If doesn't support shell syntax, but that's pretty trivial I think.
var command_str = "cat lines.txt | wc -l";
var cmds = command_str.split(/\s?\|\s?/);
var cmd = $p(cmds.shift());
while(cmds.length) cmd = cmd.pipe(cmds.shift());
cmd
.out()
.on('exit', function() {
// Do whatever
});
There's an example in the node docs for the child_process module:
Example of detaching a long-running process and redirecting its output to a file:
var fs = require('fs'),
spawn = require('child_process').spawn,
out = fs.openSync('./out.log', 'a'),
err = fs.openSync('./out.log', 'a');
var child = spawn('prg', [], {
detached: true,
stdio: [ 'ignore', out, err ]
});
child.unref();

Resources