In Node.js, why does my spawn command produce an error? - node.js

When I run:
unzip -p /tmp/document.docx word/document.xml | sed -e 's/<\/w:p>/\\n/g; s/<[^>]\{1,\}>//g; s/[^[:print:]\n]\{1,\}//g'
It correctly extracts the text from my .docx file.
But when I try to wrap this in a Node.js program as follows:
const spawn = require("child_process").spawn;
const command = "unzip"; ;
const child = spawn("sh", ["-c", "unzip -p /tmp/document.docx word/document.xml | sed -e 's/<\/w:p>/\\n/g; s/<[^>]\{1,\}>//g; s/[^[:print:]\n]\{1,\}//g'"]);
const stdout = child.stdout;
const stderr = child.stderr;
const output = "";
stderr.on("data", function(data) {
console.error("error on stderr", data.toString());
});
stdout.on("data", function(data) {
output += data;
});
stdout.on("close", function(code) {
});
I get the following error message:
error on stderr sed: -e expression #1, char 10: unknown option to `s'
How do I fix this error?

When using a command line that way in your code, you have to think about the interpretation of the \ made by node.js and antislash the antislash. One for the node.js one for the sed command.
spawn("sh", ["-c", "unzip -p /tmp/document.docx word/document.xml | sed -e 's/<\\/w:p>/\\\\n/g; s/<[^>]\\{1,\\}>//g; s/[^[:print:]\\n]\\{1,\\}//g'"])
Look at here
#T.J Crowder
In JavaScript, the backslash has special meaning both in string
literals and in regular expressions. If you want an actual backslash
in the string or regex, you have to write two: \.

Related

node exec awk command quotations

Using this pattern to pass a parameter in exec command:
const the_xml_file = 'pubmed22n1171.xml';
const the_command = "echo " + the_xml_file;
async function myBash() {
try {
const { stdout, stderr } = await exec( the_command );
console.log(stdout);
} catch (err){
console.error(err);
//console.log('stderr:', stderr);
};
};
However when the command is
const the_command = "awk 'BEGIN{splitno=15000; sn=splitno+1; out=FILENAME\"_\"int(c++/sn)+1\".xml\"} /<PubmedArticle/{f=1} f{print > out} /<\/PubmedArticle>/&&c%sn==0{close(out); f=0}' " + the_xml_file;
I get an error:
Error: Command failed: awk 'BEGIN{splitno=15000; sn=splitno+1; out=FILENAME"_"int(c++/sn)+1".xml"} /<PubmedArticle/{f=1} f{print > out} /</PubmedArticle>/&&c%sn==0{close(out); f=0}' pubmed22n1171.xml
awk: non-terminated regular expression &&c%sn==0{... at source line 1
context is
BEGIN{splitno=15000; sn=splitno+1; out=FILENAME"_"int(c++/sn)+1".xml"} /<PubmedArticle/{f=1} f{print > out} /</PubmedArticle>/&&c%sn==0{close(out); >>> f=0} <<<
Ive tested the awk command and it works from the console so I'm assuming this is a quotation mark issue. Any suggestions would be appreciated.

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

How to "grep" body of console.log?

I am totally new to NodeJS and right now I am just doing try and error based on code I am finding.
I want to monitor my router at home. To get data, I have to do a http request via nodejs and have to store a "token" to a variable. This token is set to the end of the real request for the data.
The output of curl is:
[long html stuff]
var _httoken = 12391231849184ß
[even longer html stuff]
In bash curl I would do it like:
token=`curl -v --silent http://192.168.2.1/html/login/status.html?lang=de 2>&1 | grep _httoken | grep -o '[0-9]\+'
But when I try to get it like this:
const { exec } = require('child_process');
exec('curl -v --silent http://192.168.2.1/html/login/status.html?lang=de 2>&1 | grep _httoken | grep -o '[0-9]\+'', (err, stdout, stderr) => {
if (err) {
// node couldn't execute the command
return;
}
// the *entire* stdout and stderr (buffered)
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
});
I get:
exec('curl -v --silent http://192.168.2.1/html/login/status.html?lang=de 2>&1 | grep _httoken | grep -o '[0-9]\+'', (err, stdout, stderr) => {
SyntaxError: Invalid or unexpected token
At the \.
How can I get the digits stored?
Thanks in advance

Spawn in nodejs for a unix command with spaces in parameters

I would like to execute the following command using nodejs Spawn on a Debian system :
/usr/bin/apt-get upgrade -s | tail -1 | cut -f1 -d' '
I want to use spawn and not exec because of future use of root only commands and i don't want to allow a full shell access (i will update the visudo file with correct commands)
Here is my code
const apt = spawn('/usr/bin/apt-get', ['upgrade', '-s']);
const tail = spawn('tail', ['-1']);
const cut = spawn('cut', ['-f1', '-d" "']);
apt.stdout.on('data', (data) => {
tail.stdin.write(data);
});
tail.stdout.on('data', (data) => {
cut.stdin.write(data);
});
cut.stdout.on('data', (data) => {
console.log(data.toString());
});
apt.stderr.on('data', (data) => {
console.log("apt stderr: ${data}");
});
tail.stderr.on('data', (data) => {
console.log("tail stderr: ${data}");
});
cut.stderr.on('data', (data) => {
console.log("cut stderr: ${data}");
});
apt.on('close', (code) => {
if (code !== 0) {
console.log("apt process exited with code ${code}");
}
});
tail.on('close', (code) => {
if (code !== 0) {
console.log("tail process exited with code ${code}");
}
});
cut.on('close', (code) => {
if (code !== 0) {
console.log("cut process exited with code ${code}");
}
});
res.status(200).json('');
Once executed i have an error because of the '-d" "' parameter that is not recognized. I try escaping the space with a double \ or split the parameter in both but still errors
It should just be:
const cut = spawn('cut', ['-f1', '-d ']);
No double quotes or backslash escapes -- those are for the use of the shell, not cut, and there's no shell here.
This makes dealing with unknown filenames (for your future use cases) particularly easy: When your strings are passed as arguments (to software that doesn't misuse them my running eval-equivalent code later), you don't need to quote, escape, sanitize, or otherwise modify them before they can be passed as data.
(That is to say -- when you tell your shell cut -f1 -d" ", the actual syscall it invokes to start that cut process, in C syntax, looks like execve("/usr/bin/cut", {"cut", "-f1", "-d ", NULL}, environ); the quotes were syntactic, consumed by the shell when it used them to make the decision that the space after the -d should be part of the same literal argument).

Node child_process.spawn multiple commands

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

Resources