Send user input from NodeJS to shell script - javascript

I have this NodeJS script:
var util = require('util'),
process = require('child_process'),
ls = process.exec('test.sh');
ls.stdout.on('data', function (data) {
console.log(data.toString());
ls.stdin.write('Test');
});
and this shell script:
#!/bin/bash
echo "Please input your name:";
read name;
echo "Your name is $name";
I tried to run the NodeJS script and it stucked at "Please input your name:". Does anyone know how to send an input from NodeJS script to the shell script ?
Thanks

You will have to say something like this:
ls.stdin.write('test\n');
OR
you can inherit standard streams if you want input from user using spawn.
like this:
var spawn = require('child_process').spawn;
spawn('sh',['test.sh'], { stdio: 'inherit' });

Did you try adding '\n' to the end of your input (e.g. ls.stdin.write('Test\n');) to simulate pressing return/enter?
Also, you want process.spawn, not process.exec. The latter does not have a streaming interface like you are using, but it instead executes the command and buffers stdout and stderr output (passing it to the callback given to process.exec()).

Related

Detect end of console output in node.js

var cp = require("child_process");
var proc = cp.spawn("cmd");
proc.stdout.on("data", data => console.log("Data:", data.toString()));
proc.stdin.write("help\n");
proc.stdin.write("help\n");
In the above code snippet, how would you detect when the stream has finished writing for a specific command (i.e. when, if these commands were executed in the terminal, it would show a blinking cursor that you could enter text into)?
I have tried using listening to the event end, but this seems only to be fired when the process finishes.
There is no definitive way to know, unless the program you're executing has a well-defined output format (e.g. newline-delimited JSON, XML, etc.). Either way, you will have to perform some kind of parsing (and possible buffering) of the program output.
This can be solved by giving stdin a string which will be echoed back to you when the original command has been executed, where you know that the string executed have no unwanted effects and will probably not be outputted into stdout with the output of the command e.g. for the given example:
var cp = require("child_process");
var proc = cp.spawn("cmd");
proc.stdout.on("data", data => {
var str = data.toString();
console.log(str)
if(str.search("string to be detected") !== -1){
console.log("Command finished!");
}
});
proc.stdin.write("help\n");
proc.stdin.write("string to be detected\n");
Alternatively you could wait for some feature of the response which indicates the end of the command output such as a newline or a new prompt, as suggested by #mesdex and #user866762

Call Perl script from Thunderbird Javascript

I wrote a perl script that handles some data automatically. However, I face a problem when I try to call the script from my thunderbird extension that is naturally written in javascript.
var file = Components.classes["#mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath("/usr/bin/perl");
// create an nsIProcess
var process = Components.classes["#mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(file);
// Run the process.
// If first param is true, calling thread will be blocked until
// called process terminates.
// Params are used to pass command-line arguments
// to the process
var args = ["package/myperlscript.pl", "some arguments];
process.run(true, args, args.length);
I guess I have the perl script placed at the wrong location. I tried various ones, but I could not get it work. If that is my major mistake, where is the base path that the javascript file expects?

How to print CasperJS debugging results to text file

I can't read the debugging issues in the command prompt. Can I send that to a text file in my PhantomJS or CasperJS scripts?
Here is my python code;
import os
import subprocess
#proxies = {'https': 'http://wmhproxy2:8080'}
APP_ROOT = os.path.dirname(os.path.realpath(__file__))
CASPER = "C:/casperjs/bin/casperjs"
#SCRIPT = os.path.join(APP_ROOT,'unicorn.js')
SCRIPT = os.path.join(APP_ROOT,'unicorn.js')
params = CASPER +' '+ SCRIPT
paper = subprocess.check_output(params,shell=True)
rock = paper.text
salesforce = open('S:/N/salesforce2.txt','w')
salesforce.write(write)
print(subprocess.check_output(params,shell=True))
CasperJS script:
var casper = require('casper').create({
verbose: true,
logLevel: "debug"
});
var x = require('casper').selectXPath;
casper.options.waitTimeout = 7000;
casper.start('http://www.click2houston.com/',function(){
this.echo(this.getTitle());
}).viewport(1200,1000);
casper.run();
Yes, you can append the logs to a file in your CasperJS script.
CasperJS has an internal event management which exposes the log event. A simple object is passed into the event handler which also contains the log message. If you want a different formatting of the logs, then you need to do it yourself or implement a part of the log code in your event handler.
var fs = require("fs");
fs.write("mylogfile.log", "", "w"); // overwrite log file
casper.on("log", function(entry){
fs.write("mylogfile.log", entry.message + "\n", "a");
});
CasperJS is built on top of PhantomJS, so you can directly use PhantomJS' file system module. Of course you can also pass the log file as a commandline option into the script and get it out through casper.cli.

Run Bash Script with Node from client request

I need to make a server-side script run when a user from the browser clicks a button...
I've been researching for a while, and can't figure it out.
What we have:
Node.js server (on localhost) running on Fedora Red Hat
NO PHP
Most pages are html + javascript + jQuery
To be more clear, here is what we'd like to happen:
-->User goes to http:// localhost /index.html
-->User selects colors, pushes "submit" button.
-->Selected colors go to the bash script (on the server) ./sendColors [listOfColors]
-->The bash script does it's thing.
================
Things I've tried
child_process.spawn
I WISH I could do this on the html page:
var spawn = require('child_process').spawn;
ls = spawn(commandLine, [listOfColors]);
ls.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
ls.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
ls.on('close', function (code) {
console.log('child process exited with code ' + code);
});
but this script is server-side, not client-side, so I can't run it on the html page (I believe). The error I get when I try to run this is that require is undefined.
browserify
I've tried installinst browserify, but the machine we are using isn't connected to the internet, and cannot use npm install. I've manually copied over the files to usr/lib and "required" it fine, but then it said that it couldn't find require "through", which is in browserify's index.js...
getRuntime
tried this thing:
var bash_exit_code = 0; // global to provide exit code from bash shell invocation
function bash(command)
{
var c; // a character of the shell's stdout stream
var retval = ""; // the return value is the stdout of the shell
var rt = Runtime.getRuntime(); // get current runTime object
var shell = rt.exec("bash -c '" + command + "'"); // start the shell
var shellIn = shell.getInputStream(); // this captures the output from the command
while ((c = shellIn.read()) != -1) // loop to capture shell's stdout
{
retval += String.fromCharCode(c); // one character at a time
}
bash_exit_code = shell.waitFor(); // wait for the shell to finish and get the return code
shellIn.close(); // close the shell's output stream
return retval;
}
said it didn't know what Runtime was
RequireJS
I've looked into RequireJS, but didn't understand how to use it in my case
eval
I've tried eval also... but I think that's for algebric expressions... didn't work.
ActiveX
even tried activeX:
variable=new ActiveXObject(...
said it didn't know what ActiveXObject is
================
Currently what I'm trying
HttpServer.js:
var http = require('http');
...
var colors = require('./colorsRequest.js').Request;
...
http.get('http://localhost/colorsRequest', function(req, res){
// run your request.js script
// when index.html makes the ajax call to www.yoursite.com/request, this runs
// you can also require your request.js as a module (above) and call on that:
res.send(colors.getList()); // try res.json() if getList() returns an object or array
console.log("Got response ");// + res.statusCode);
});
colorsRequest.js
var RequestClass = function() {
console.log("HELLO");
};
// now expose with module.exports:
exports.Request = RequestClass;
index.html
...
var colorsList = ...
...
$.get('http://localhost/colorsRequest', function(colors) {
$('#response').html(colorsList); // show the list
});
I'm getting
GET http://localhost/colorsRequest 404 (Not Found)
Anyone got any ideas?
Here's a simple boilerplate for the server (which uses Express, so you might need to install that first: npm install express):
var spawn = require('child_process').spawn;
var express = require('express');
var app = express();
app.use(express.static(__dirname));
app.get('/colorsRequest', function(req, res) {
var command = spawn(__dirname + '/run.sh', [ req.query.color || '' ]);
var output = [];
command.stdout.on('data', function(chunk) {
output.push(chunk);
});
command.on('close', function(code) {
if (code === 0)
res.send(Buffer.concat(output));
else
res.send(500); // when the script fails, generate a Server Error HTTP response
});
});
app.listen(3000);
You can pass it a color, and it will run the shellscript run.sh (of which it assumes is located in the same directory as the server JS file) with the color passed as argument:
curl -i localhost:3000/colorsRequest?color=green
# this runs './run.sh green' on the server
Here's a boilerplate HTML page (save it as index.html, put it in the same directory as the server code and the shell script, start the server, and open http://localhost:3000 in your browser):
<!doctype html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<select>
<optgroup label="Pick a color:">
<option>green</option>
<option>blue</option>
<option>yellow</option>
<option>orange</option>
</optgroup>
</select>
<script>
$('select').on('change', function() {
$.get('/colorsRequest', { color : $(this).val() });
});
</script>
</body>
</html>
You are on the right way with your first approach, the child_process.spawn variant. Ofcourse you can't put this in the HTML page, as it is then executed in the browser, not in the server, but you can easily create a request in the browser (AJAX or page load, depending on what you need), that triggers running this script in the server.

How do I escape a string for a shell command in node?

In nodejs, the only way to execute external commands is via sys.exec(cmd). I'd like to call an external command and give it data via stdin. In nodejs there does yet not appear to be a way to open a command and then push data to it (only to exec and receive its standard+error outputs), so it appears the only way I've got to do this right now is via a single string command such as:
var dangerStr = "bad stuff here";
sys.exec("echo '" + dangerStr + "' | somecommand");
Most answers to questions like this have focused on either regex which doesn't work for me in nodejs (which uses Google's V8 Javascript engine) or native features from other languages like Python.
I'd like to escape dangerStr so that it's safe to compose an exec string like the one above. If it helps, dangerStr will contain JSON data.
This is what I use:
var escapeShell = function(cmd) {
return '"'+cmd.replace(/(["'$`\\])/g,'\\$1')+'"';
};
You should never rely on escaping unknown input going to a shell parameter - there will almost always be some edge-case that you haven't thought of that allows the user to execute arbitrary code on your server.
Node has support for calling a command and passing each argument separately, with no escaping required. This is the safest way to do it:
const { spawn } = require('child_process');
// Note that the arguments are in an array, not using string interpolation
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
The documentation is here
If you need simple (yet correct) solution you can use this:
function escapeShellArg (arg) {
return `'${arg.replace(/'/g, `'\\''`)}'`;
}
So your string will be simply escaped with single quotes as Chris Johnsen mentioned.
echo 'John'\''s phone';
It works in bash because of strong quoting, feels like it also works in fish, but does not work in zsh and sh.
If you have bash your can run your script in sh or zsh with 'bash -c \'' + escape('all-the-rest-escaped') + '\''.
But actually... node.js will escape all needed characters for you:
var child = require('child_process')
.spawn('echo', ['`echo 1`;"echo $SSH_TTY;\'\\0{0..5}']);
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
this block of code will execute:
echo '`echo 1`;"echo $SSH_TTY;'\''\\0{0..5}'
and will output:
stdout: `echo 1`;"echo $SSH_TTY;\'\\0{0..5}
or some error.
Take a look at http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
By the way simple solution to run a bunch of commands is:
require('child_process')
.spawn('sh', ['-c', [
'cd all/your/commands',
'ls here',
'echo "and even" > more'
].join('; ')]);
Have a nice day!
I second the opinion of Will, whenever possible you should avoid escaping by hand and prefer spawn.
However, in the case that escaping is unavoidable, for example if you need to use exec or you are executing a command through ssh. Then you can use base64 to pass safe characters to bash and rely on bash to escape the unknown.
const dangerStr = 'bad stuff here'
// base64 has safe characters [A-Za-z=0-9+/]
const dangerBase64 = btoa(dangerStr)
sys.exec(`echo "$(echo ${dangerBase64} | base64 -d)" | somecommand`)
The explanation is the following:
dangerBase64 is unknown but it does not contain unsafe characters in bash. Hence echo ${dangerBase64} will output what we want.
Finally the double quote around $(echo ${dangerBase64} | base64 -d) escape the actual value passed by the user inside bash, which is safe and has the same value that the user wanted.
If you also need to deal with special character (line-breaks etc.) you can do it this way:
str = JSON.stringify(str)
.replace(/^"|"$/g,'') //remove JSON-string double quotes
.replace(/'/g, '\'"\'"\'') //escape single quotes the ugly bash way
This assumes you use Bash's strong-quoting via single-quotes) and the receiver can understand JSON's C-like escaping.
If you are building you own software, you can encode the command to base64 or hex format then decode the arguments from the program.
For my Nodejs applications I use.
var base64_encode = exports.base64_encode = function(non_base64_string){
return Buffer.from(non_base64_string).toString('base64');
}
var base64_decode = exports.base64_decode = function(base64_string){
return Buffer.from(base64_string, 'base64').toString('ascii')
}
So when I run a base64 encoded command like this
webman grep --search "aW5jbHVkZV9vbmNlICRfU0VSVkVSWyJET0NVTUVOVF9ST09UIl0uIi9zZXR0aW5ncy5waHAiOw==" --replacement "JGRvY3VtZW50X3Jvb3QgPSBfX0RJUl9fO3doaWxlKHRydWUpe2lmIChmaWxlX2V4aXN0cygkZG9jdW1lbnRfcm9vdC4iL3NldHRpbmdzLmpzb24iKSl7YnJlYWs7fWVsc2V7JGRvY3VtZW50X3Jvb3Q9ZGlybmFtZSgkZG9jdW1lbnRfcm9vdCk7fX08bmV3bGluZT5pbmNsdWRlX29uY2UgJGRvY3VtZW50X3Jvb3QuIi9zZXR0aW5ncy5waHAiOw=="
I can get the arguments search and replacement arguments without stress using base64_decode
There is a way to write to an external command: process.createChildProcess (documentation) returns an object with a write method. createChildProcess isn't as convenient though, because it doesn't buffer stdout and stderr, so you will need event handlers to read the output in chunks.
var stdout = "", stderr = "";
var child = process.createChildProcess("someCommand");
child.addListener("output", function (data) {
if (data !== null) {
stdout += data;
}
});
child.addListener("error", function (data) {
if (data !== null) {
stderr += data;
}
});
child.addListener("exit", function (code) {
if (code === 0) {
sys.puts(stdout);
}
else {
// error
}
});
child.write("This goes to someCommand's stdin.");

Categories