NodeJS Child Process execute 2 command in the same shell - node.js

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

Related

Node.js - exec shell commands with .bashrc initialized

I'm writing a small utility tool for development to sync files over ssh. Normally I use ssh-agent set up in .bashrc file to connect to my dev server easily. I'd like to use exec in the script, but calling ssh-agent, every time I make a request sounds a bit inoptimal.
Is there a way I could execute the agent code once, and then have it working for all subsequent ssh requests I make? E.g. to spawn a shell process like a terminal emulator, and then use that process to execute a command, rather than invoking a new shell with each command.
The reason I want to do this, is I don't want to store the password in a config file.
You can create one ssh process, and then execute other commands using same process. Here is an example how to use it for bash. I'm creating a new bash shell and execugte the command ls -la and exit you can execute other commands.
const cp = require("child_process")
class MyShell {
constructor(command) {
this._spawned = cp.spawn(command, {
stdio: ["pipe", "pipe", "inherit"],
})
}
execute(command, callback) {
this._spawned.stdin.write(command + "\n")
this._spawned.stdout.on("data", (chunk) => {
if (callback) {
callback(chunk.toString())
}
})
}
}
var myShell = new MyShell("bash")
myShell.execute("ls -la", (result) => {
console.log(result)
})
myShell.execute("exit")

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

PM2 breaks child process bash script

I have a place in my code where I need to spawn a bash script, write to its standard input, and read from its standard output.
I do this using node's child_process.spawn.
Unfortunately, when I run this code under pm2, the bash script hangs forever when calling mkdir.
Is there any way to avoid this issue?
test.js
'use strict';
const child_process = require('child_process');
setInterval(() => {
const process = child_process.spawn('./test.sh');
process.on('exit', () => {
console.log('process exit');
});
process.stdout.on('data', (data) => {
console.log('Output: ' + data.toString('utf8'));
});
}, 1000);
test.sh
#!/usr/bin/env bash
TEMP=$(mktemp -d);
echo "Created directory $TEMP"
rm -rf ${TEMP}
echo "Deleted directory $TEMP"
Expected output
Starting
Spawning test.sh
Output: Created directory /tmp/tmp.I6Buifdmlu
Output: Deleted directory /tmp/tmp.I6Buifdmlu
process exit
Actual output
[STREAMING] Now streaming realtime logs for [test] process
2|test | Spawning test.sh
2|test | Spawning test.sh
Environment
OS: Ubuntu 14.04
Node: 4.7.3
PM2: 2.4.0
NB: I have tested this on Mac OSX and there is no problem

Run block of bash / shell in node's spawn

I'm writing a command line utility, and I need stdout to write to TTY or use {stdio: 'inherit'} I've been getting by with exec but it's not going to cut it. I need way for a spawn process to execute the following echo commands below. I know that spawn spins up a child process with a given command, and you pass in arguments, but I need it to just take a line-separated string of commands like this. This is what I'm currently feeding to exec. Is this possible?
const spawn = require('child_process').spawn
const child = spawn(`
echo "alpha"
echo "beta"
`)
child.stdout.on('data', (data) => {
console.log(`stdout: ${data}`)
});
child.stderr.on('data', (data) => {
console.log(`stderr: ${data}`)
});
child.on('close', (code) => {
console.log(`child process exited with code ${code}`)
});
spawn() does not involve a shell, so in order to have it execute shell commands, you must invoke the shell executable explicitly and pass the shell command(s) as an argument:
const child = spawn('/bin/sh', [ '-c', `
echo "alpha"
echo "beta"
` ])
Note I've used /bin/sh rather than /bin/bash in an effort to make your command run on a wider array of [Unix-like] platforms.
All major POSIX-like shells accept a command string via the -c option.

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