Node child_process.spawn multiple commands - node.js

I wan to automate the creation and extracting of keystore.
The problem I'm facing is how to join the commands using the ' | ' symbol or similar solution.
//Original Command
var command='keytool -exportcert -storepass mypass -keypass mypass
-alias myalias -keystore mykey.keystore | openssl sha1 -binary | openssl base64';
//Arguments for the spawn
var keyArgs = [
'-exportcert',
'-storepass','mypass',
'-keypass','mypass',
'-alias','myalias',
'-keystore',"myjey.keystore",
'openssl','sha1',
'-binary',
'openssl','base64',
];
exec('keytool',keyArgs,{cwd:appCreateFolder+"/"+opt.id+"/Certificates"},function(e){
console.log(chalk.cyan('Key created'));
})

From Node.js v6 you can specify a shell option in spawn method which will run command using shell and thus it is possible to chain commands using spawn method.
For example this:
var spawn = require('child_process').spawn;
var child = spawn('ls && ls && ls', {
shell: true
});
child.stderr.on('data', function (data) {
console.error("STDERR:", data.toString());
});
child.stdout.on('data', function (data) {
console.log("STDOUT:", data.toString());
});
child.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
});
Will trigger an error on node.js version less than 6:
Error: spawn ls && ls && ls ENOENT
But on version 6 and higher it will return expected result:
node app.js
STDOUT: app.js
STDOUT: app.js
app.js
Child exited with code: 0

The | symbol on the command line is called "piping" because it's like piping streams of data together. What you want is to get ahold of the stdin (Standard In) and stdout (Standard Out) streams for the commands you're executing.
For example, this is how you would spawn the echo command and pipe it's output to grep:
var spawn = require('child_process').spawn;
var echo = spawn('echo', ['The quick brown fox\njumped over the lazy dog.']);
var grep = spawn('grep', ['brown']);
echo.stdout.pipe(grep.stdin);
grep.stdout.pipe(process.stdin);
The above example spawns both the "echo" and "grep" commands. It pipes any output from the echo process's stdout stream to the grep process's stdin stream. Finally we pipe the grep process's stdout stream to the parent process's (your node process) stdin stream so you can see the output in your terminal.
The output would be "The quick brown fox" because I put a newline character in the middle and the grep only matched the first line containing "brown".
You could use the exec function to achieve the same result. Just might be harder to maintain in the future, but if all you need is to quickly run a set of piped commands, you can enter the full command line string (including pipe symbols) and pass it to exec.
var exec = require('child_process').exec;
var cmdString = 'grep "The quick brown fox\njumped over the lazy dog." | grep "brown"';
exec(cmdString, (err, stdout, stderr) => {
console.log(stdout);
});
Or instead of passing in the callback function you could just pipe the output to process.stdin if all you care about is seeing the command output.
exec(cmdString).stdout.pipe(process.stdin);
Here's a quick example of what I believe your code should look like using spawn. May require tweaks since it seems specific to what you're doing.
var keyArgs = [
'-exportcert',
'-storepass','mypass',
'-keypass','mypass',
'-alias','myalias',
'-keystore',"myjey.keystore",
'openssl','sha1',
'-binary',
'openssl','base64',
];
var keyOpts = {
cwd: `${appCreateFolder}/${opt.id}/Certificates`
};
var spawn = require('child_process').spawn;
var keytool = spawn('keytool', keyArgs, keyOpts);
var opensslBinary = spawn('openssl', ['sha1', '-binary']);
var opensslBase64 = spawn('openssl', ['base64']);
keytool.stdout.pipe(opensslBinary.stdin);
opensslBinary.stdout.pipe(opensslBase64.stdin);
opensslBase64.stdout.pipe(process.stdin);
opensslBase64.on('close', () => {
console.log(chalk.cyan('Key created.'));
});
Or using exec:
var exec = require('child_process').exec;
var cmdString = 'keytool -exportcert -storepass mypass -keypass mypass -alias myalias -keystore mykey.keystore | openssl sha1 -binary | openssl base64';
var cmdOpts = {
cwd: `${appCreateFolder}/${opt.id}/Certificates`
};
exec(cmdString, cmdOpts, () => {
console.log(chalk.cyan('Key created.'));
});

Related

Spawn command with redirection

Let say I have this command
somecli -s < "/path/to/file.txt"
How can I convert the above command to NodeJS spawn command ? I did something like this, but seems like it didn't pass the input.
spawn('somecli', ['-s', '<', '"/path/to/file.txt"'], { stdio: 'inherit'}).on('error', function (error) {
// something
});
I can use the exec command below and it's working, but I prefer if we can see the live output.
exec('somecli -s < "/path/to/file.txt"', (e, stdout, stderr) => {
// something
})
something like this should help
const { spawn } = require('child_process');
const fs = require('fs');
const writeStream = fs.createWriteStream("/path/to/file.txt");
const shell = spawn('somecli', ['-s']);
shell.stdout.pipe(writeStream);
To pass file input to command ( STDIN redirection )
$ somecli -s < /path/to/file.txt
We can do it something like this
spawn('somecli', ['-s'], {stdio: [fs.openSync('/path/to/file.txt', 'r'), process.stdout, process.stderr]});
To pass command output to file ( STDOUT redirection )
$ somecli -s > /path/to/file.txt
You may follow Ashish answer
let s = spawn('somecli', ['-s])
s.stdout.pipe(fs.createWriteStream('/path/to/file.txt'))

Node js Child_process not returning stdout

Not sure why this does not work, if I run a simple command such as cmd='ls -all' then I get the output back, but when I use this to run a command which takes some time to complete I don't get anything at all returned.
In this example, I am using lftp to mirror some folders and want to get the reply, if I run the command from the terminal then of course I see the output, but using child process I get nothing:
var childProcess = require('child_process');
var cmd = 'lftp sftp://user:password#somehost -e "mirror -R --delete --parallel=5 /usr/share/scripts/ /volumes/folders/usr/share/;bye"';
childProcess.exec(cmd, function (error, stdout, stderr) {
console.log('stdout:'+stdout);
console.log('stderr:'+stderr);
console.log('error:'+error);
});
I also tried the spawn method, nothing returned from that either:
var spawn = require('child_process').spawn;
var lftp = spawn('lftp',['sftp://user:password#somehost', '-e "mirror -R --delete --parallel=5 /usr/share/scripts/ /volumes/folders/usr/share/;bye"']);
lftp.stdout.on('data', function(data) {
console.log(data.toString());
});
I think the problem is that lftp buffers its output.
To get around that you'll need to use unbuffer (http://expect.sourceforge.net/example/unbuffer.man.html) to send the output directly to stdout.

how do I make node child_process exec continuously

How to exec continuously? e.g. ls after cd?
I tried
exec = require('child_process').exec;
exec('cd ~/',
function(){
exec('ls'),
function(err, stdout, stderr){
console.log(stdout); // this logs current dir but not ~/'s
}
}
)
exec('cd ~/').exec('ls', function(err, stdout, stderr){
console.log(stdout);
})//this also fails because first exec returns a ChildProcess Object but not itself.
It is not possible to do this because exec and spawn creates a new process. But there is a way to simulate this. You can start a process with exec and execute multiple commands in the same time:
In the command line if you want to execute 3 commands on the same line you would write:
cmd1 & cmd2 & cmd3
So, all 3 commands run in the same process and have access to the context modified by the previous executed commands.
Let's take your example, you want to execute cd ../ and after that to execute dir and to view the previous directory list.
In cmd you shoud write:
cd../ & dir
From node js you can start a process with exec and to tell it to start another node instance that will evaluate an inline script:
var exec = require('child_process').exec;
var script = "var exec = require('child_process').exec;exec('dir',function(e,d,er){console.log(d);});";
script = '"'+script+'"';//enclose the inline script with "" because it contains spaces
var cmd2 = 'node -e '+script;
var cd = exec('cd ../ &'+cmd2,function(err,stdout,strerr)
{
console.log(stdout);//this would work
})
If you just want to change the current directory you should check the documentation about it http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
You can use nodejs promisify and async/await:
const { promisify } = require('util');
const exec = promisify(require('child_process').exec);
export default async function () {
const cpu = await exec('top -bn1');
const disk = await exec('df -h');
const memory = await exec('free -m');
const payload = {
cpu,
disk,
memory,
};
return payload
}
If you want to use cd first, better use process.chdir('~/'). Then single exec() will do the job.
You can call exec with cwd param like so:
exec('ls -a', {
cwd: '/Users/user'
}, (err, stdout) => {
if (err) {
console.log(err);
} else {
console.log(stdout);
}
})
But beware, cwd doesn't understand '~'. You can use process.env.HOME instead.

how to tail multiple files in node.js?

when use the following code to tail a file, we can successfully output data.
var spawn = require('child_process').spawn;
var filename = '/logs/error.log';
var tail = spawn("tail", ["-f", filename]);
tail.stdout.on("data", function (data) {
console.log(data);
});
but when i change filename to "/logs/*.log", i don't find anything output. who can tell me what is the reason? Thanks!
When typing tail -f /logs/*.log on the console, the expansion of /logs/*.log is handled by the shell; by the time the tail program gets the arguments, they've already been expanded to tail -f /logs/error.log /logs/other.log. You need to do the expansion yourself for Node:
var fs = require('fs');
var spawn = require('child_process').spawn;
var filename = fs.readdirSync('/logs').map(function(file) { return '/logs/' + file });
var tail = spawn("tail", ["-f"].concat(filename));
tail.stdout.on("data", function (data) {
console.log(data);
});
Because neither tail nor spawn know how to expand file names with wild cards into the set of matching file names. That's normally performed by the shell, so in this case you'll need to do it yourself in code.

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