How to run interactive shell command inside node.js? - javascript

I have to run some interactive shell command inside node.js. Lets our interactive shell be $ python:
var cp = require('child_process');
var pythonChildProcess = cp.spawn('python');
pythonChildProcess.stdout.on("data", function(data) {
console.log('data successfully written!', data); // never outputs anything
});
pythonChildProcess.stdin.write('1 + 1');
pythonChildProcess.stdin.end();
This code does not output anything (but stdout should be 2).
But if it would, there will be another problem: how to make it interactive? The process ends when I call pythonChildProcess.stdin.end();! But I just wanted to end stdin and write next stdin!
UPD:
If I could emulate pressing of enter button - I would be able to interactively write to the process. But adding \n to the end of the input string does not help.

This works great for me:
const { spawn } = require('child_process')
const shell = spawn('sh',[], { stdio: 'inherit' })
shell.on('close',(code)=>{console.log('[shell] terminated :',code)})

First and foremost, one of the things preventing node from interfacing with other interactive shells is that the child application must keep its "interactive" behavior, even when stdin doesn't look like a terminal. python here knew that its stdin wasn't a terminal, so it refused to work. This can be overridden by adding the -i flag to the python command.
Second, as you well mentioned in the update, you forgot to write a new line character to the stream, so the program behaved as if the user didn't press Enter.
Yes, this is the right way to go, but the lack of an interactive mode prevented you from retrieving any results.
Here's something you can do to send multiple inputs to the interactive shell, while still being able to retrieve each result one by one. This code will be resistant to lengthy outputs, accumulating them until a full line is received before performing another instruction. Multiple instructions can be performed at a time as well, which may be preferable if they don't depend on the parent process' state. Feel free to experiment with other asynchronous structures to fulfil your goal.
var cp = require('child_process');
var childProcess = cp.spawn('python', ['-i']);
childProcess.stdout.setEncoding('utf8')
var k = 0;
var data_line = '';
childProcess.stdout.on("data", function(data) {
data_line += data;
if (data_line[data_line.length-1] == '\n') {
// we've got new data (assuming each individual output ends with '\n')
var res = parseFloat(data_line);
data_line = ''; // reset the line of data
console.log('Result #', k, ': ', res);
k++;
// do something else now
if (k < 5) {
// double the previous result
childProcess.stdin.write('2 * + ' + res + '\n');
} else {
// that's enough
childProcess.stdin.end();
}
}
});
childProcess.stdin.write('1 + 0\n');

A tl;dr version of #E_net4's answer, for those who understand just by reading the code. For a detailed explanation, please do read his answer. He has described it well.
var spawn = require('child_process').spawn
var p = spawn('node',['-i']);
p.stdout.on('data',function (data) {
console.log(data.toString())
});
p.stdin.write('1 + 0\n');
Output:
>
1

Related

Running stateful commands in PowerShell through Node.js

Context: I have a javascript file that activates PowerShell's native SpeechSynthesizer module. The script receives a message and passes that through to PowerShell, where it is rendered as speech.
Problem: there is horrible latency (~5sec) between execution and response. This is because the script creates an entirely new PowerShell session and SpeechSynthesizer object with every execution.
Objective: I want to change the script so that a single PowerShell session and SpeechSynthesizer object is persisted and used across multiple sessions. I believe this will eradicate the latency completely.
Limiting Factor: this modification requires making the PowerShell execution stateful. Currently, I don't know how to incorporate stateful commands for the PowerShell in a javascript file.
Code:
const path = require('path');
const Max = require('max-api');
const { exec } = require('child_process');
// This will be printed directly to the Max console
Max.post(`Loaded the ${path.basename(__filename)} script`);
const execCommand = command => {
// Max.post(`Running command: ${command}`);
exec(command, {'shell':'powershell.exe'}, (err, stdout, stderr) => {
if (err) {
// node couldn't execute the command
Max.error(stderr);
Max.error(err);
return;
}
// the *entire* stdout and stderr (buffered)
Max.outletBang()
});
}
// Use the 'outlet' function to send messages out of node.script's outlet
Max.addHandler("speak", (msg) => {
let add = 'Add-Type -AssemblyName System.speech'
let create = '\$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer'
let speak = `\$speak.Speak(\'${msg}\')`
let command = ([add,create,speak]).join('; ')
execCommand(command)
});
Objective, Re-stated: I want to move the add and create commands to a 'create' handler which will only be ran once. The speak command will be run an arbitrary amount of times afterward.
Attempted Solution: I've found one package (https://github.com/bitsofinfo/powershell-command-executor) that supposedly supports stateful PowerShell commands, but it's very complicated. Also, the author mentions a risk of command injection and other insecurities, of which I have no knowledge of.
Any and all suggestions are welcome. Thanks!

Using stdin with a daemon process?

I have a script that needs to be running 24/7, so I have been running it using pm2. However, I also would like to be able to occasionally check on the script. Before running it as a daemon I set it to read the stdin, but for daemons this does not work. Is there a simple way to do this and run a daemon process?
I understand that this is pretty contradictory to the nature of daemons, but I need the script to run continuously and have limited user input.
It can be done by using process.spawn, the following example is taken from the book: Professional Node.js
Create a file named plus_one.js:
// unpause the stdin stream
process.stdin.resume();
process.stdin.on('data', function(data) {
var number;
try {
// parse the input data into a number
number = parseInt(data.toString(), 10);
// increment by one
number += 1;
// output the number
process.stdout.write(number + "\n");
} catch(err) {
process.stderr.write(err.message + "\n");
}
});
You can run this simple program by calling:
$ node plus_one.js
create a file named plus_one_test.js:
var spawn = require('child_process').spawn;
// Spawn the child with a node process executing the plus_one app var
child = spawn('node', ['plus_one.js']);
// Call this function every 1 second (1000 milliseconds):
setInterval(function() {
// Create a random number smaller than 10.000
var number = Math.floor(Math.random() * 10000);
// Send that number to the child process:
child.stdin.write(number + "\n");
// Get the response from the child process and print it:
child.stdout.once('data', function(data) {
console.log('child replied to ' + number + ' with: ' + data);
});
}, 1000);
child.stderr.on('data', function(data) {
process.stdout.write(data);
});
Here you launch the +1 app as a child process on lines 1 to 4.
Then you use the setInterval function to do the following every second:
Create a random natural number smaller than 10,000. Send that number as a string to the child process. Wait for the child process to reply with a string.

How to use C# console for input and logging at the same time

I have got a C# console application. I'm starting up another cmd process to use my NodeJS app (maybe any advise on how to do this otherwise? I have to run both at the same time, they work togheter).
Now the NodeJS process its output is streamed to my C# console.
private readonly Process _nodeProcess = new Process
{
StartInfo =
{
FileName = "cmd",
Arguments = #"/K Node server.js",
UseShellExecute = false,
RedirectStandardOutput = true
}
};
internal void Start()
{
_nodeProcess.Start();
using (StreamReader reader = _nodeProcess.StandardOutput)
{
while (true)
{
string result = reader.ReadLine();
Console.WriteLine(result);
}
}
}
This works fine.
Now the problem here is, my Node logs something, but meanwhile I want to be able to give commands (input) on my console.
Example of my console output: (of course it uses a whole system, outputting real stuff..)
NodeJS process started
node log 1
node log 2
node log ...
and so on ..
But while it keeps outputting my log lines, I want to be able to type (for command input) with Console.ReadLine();. This without the risk of typing inside the same line as an output line.
I would appreciate any help. Any suggestions on improving my code are welcome.
In your case it would be better to handle the output Asynchronously.
First add an Eventhandler to your Output:
_nodeProcess.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
Then add the line _nodeProcess.BeginOutputReadLine(); after _nodeProcess.Start();
Something like:
_nodeProcess.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
_nodeProcess.Start();
_nodeProcess.BeginOutputReadLine();
var line = Console.ReadLine();
while (line != null && line.ToLower() != "exit")
{
line = Console.ReadLine();
}
_nodeProcess.Close();

Node js check if file is being used by another process

I'm working on a node-webkit app that performs filesystem operations. I need to be able to check if the file is being used by another process before deleting (unlinking).
If I only use the fs.unlink method, the system will wait until the file stops being used before removing it completely. What I want is that if the file is being used, don't wait until it's released, but cancel the whole operation and don't delete it.
Thanks.
Assuming you're on a *nix system something like this should work. Obviously this is overly simplified as far as error and path checking. There may very well be a better way to do this but I might as well start the discussion with this:
var fs = require('fs'),
exec = require('child_process').exec;
function unlinkIfUnused(path) {
exec('lsof ' + path, function(err, stdout, stderr) {
if (stdout.length === 0) {
console.log(path, " not in use. unlinking...");
// fs.unlink...
} else {
console.log(path, "IN USE. Ignoring...");
}
});
}
unlinkIfUnused('/home/jack/testlock.txt');

Javascript Logging

This is a noob question.
What if I want to add logging to the java script application, which is running in a browser (IE, FF, etc.) ? As I understand I can not save log files in the client host. So, I have only two options: display my logging information in a new browser window (like "blackbird") or send the logging to the server.
Is it correct? What kind of logging do they usually use?
You can't "store" log files on client host. You can open a window and visualize it, but you (assuming you are running the Web Application) will never see it.
If you absolutely must get client side logs, you need to send them back to the server using AJAX. Here's a blog post I really liked about it.
Another possibility is the jsnlog library http://js.jsnlog.com/
It will let you send client side logs to the server.
Take a look at https://log4sure.com (disclosure: I created it) - but it is really useful, check it out and decide for yourself. It allows you to log errors/event and also lets you create your custom log table. It stores everything on its own server so you don't have to. It also allows you to monitor your logs real-time. And the best part, its free.
You can also use bower to install it, use bower install log4sure
The set up code is really easy too:
// setup
var _logServer;
(function() {
var ls = document.createElement('script');
ls.type = 'text/javascript';
ls.async = true;
ls.src = 'https://log4sure.com/ScriptsExt/log4sure.min.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ls, s);
ls.onload = function() {
// use your token here.
_logServer = new LogServer("use-your-token-here");
};
})();
// example for logging text
_logServer.logText("your log message goes here.")
//example for logging error
divide = function(numerator, divisor) {
try {
if (parseFloat(value) && parseFloat(divisor)) {
throw new TypeError("Invalid input", "myfile.js", 12, {
value: value,
divisor: divisor
});
} else {
if (divisor == 0) {
throw new RangeError("Divide by 0", "myfile.js", 15, {
value: value,
divisor: divisor
});
}
}
} catch (e) {
_logServer.logError(e.name, e.message, e.stack);
}
}
// another use of logError in window.onerror
// must be careful with window.onerror as you might be overwriting some one else's window.onerror functionality
// also someone else can overwrite window.onerror.
window.onerror = function(msg, url, line, column, err) {
// may want to check if url belongs to your javascript file
var data = {
url: url,
line: line,
column: column,
}
_logServer.logError(err.name, err.message, err.stack, data);
};
// example for custom logs
var foo = "some variable value";
var bar = "another variable value";
var flag = "false";
var temp = "yet another variable value";
_logServer.log(foo, bar, flag, temp);

Categories