Send text to .bat file that is ran from NodeJS - node.js

I want to start a .bat file from within NodeJS. With that, I would then be able to send messages to that running bat file.
My code:
const childprocess = require("child_process")
const mybat = childprocess.exec("start cmd /c my.bat", () => {
console.log("bat file has finished")
})
// some time later in another function
mybat.send("text to send")
// within the bat, it would use the new message "text to send" as if you typed and sent a message in the cmd terminal
// ...
mybat.send("a")
// sending any key to complete a PAUSE command which will close the cmd
The .send() isn't a working function but hopefully it demonstrates what I'm trying to accomplish. Everything except the send functions works fine.

The following code uses #rauschma/stringio to asynchronously write to the stdin of a child process running a shell command:
const {streamWrite, streamEnd, onExit} = require('#rauschma/stringio');
const {spawn} = require('child_process');
async function main() {
const sink = spawn('cmd.exe', ['/c', 'my.bat'],
{stdio: ['pipe', process.stdout, process.stderr]}); // (A)
writeToWritable(sink.stdin); // (B)
await onExit(sink);
console.log('bat file has finished');
}
main();
async function writeToWritable(writable) {
...
await streamWrite(writable, 'text to send\n');
...
await streamWrite(writable, 'a');
...
await streamEnd(writable);
}
We spawn a separate process, called sink, for the shell command. writeToWritable writes to sink.stdin. It does so asynchronously and pauses via await, to avoid requiring too much buffering.
Observations:
In line A, we tell spawn() to let us access stdin via sink.stdin ('pipe'). stdout and stderr are forwarded to process.stdin and process.stderr, as previously.
We don’t await in line B for the writing to finish. Instead, we await until the child process sink is done.

Related

How do i send a large text file from node to python as a child process

I want to send the text file I upload to my node.js app to python in order to do some machine learning and be able to use the pandas and NumPy libraries.
I never worked with child process before but I found a tutorial and i came up with the following code :
app.post("/upload-txt", uploads.single("txt"), (req, res) => {
//convert csvfile to jsonArray
//const fileName = req.file.originalname;
let contents = readTextFile.readSync(req.file.path);
var largeDataSet = [];
// spawn new child process to call the python script
const python = spawn("python", ["script4.py", contents]);
// collect data from script
python.stdout.on("data", function (data) {
console.log("Pipe data from python script ...");
largeDataSet.push(data);
});
// in close event we are sure that stream is from child process is closed
python.on("close", (code) => {
console.log(`child process close all stdio with code ${code}`);
// send data to browser
res.send(largeDataSet.join(""));
});
});`
This works fine for a limited number of lines in the text file but as soon as the file gets even remotely larger it does not, how do i fix this ?
It appears that your data is sometimes too long for the command line in whatever OS you're using. So, instead of passing the data itself on the command line, you can pass the filename on the command line and then modify the python script to get the filename from the command line, read the data itself (which won't be limited by command line limitations) and then process that data.
app.post("/upload-txt", uploads.single("txt"), (req, res) => {
//convert csvfile to jsonArray
var largeDataSet = [];
// spawn new child process to call the python script
// pass the python script the uploaded file as an argument
const python = spawn("python", ["script4.py", req.file.path]);
// collect data from script
python.stdout.on("data", function (data) {
console.log("Pipe data from python script ...");
largeDataSet.push(data);
});
// in close event we are sure that stream is from child process is closed
python.on("close", (code) => {
console.log(`child process close all stdio with code ${code}`);
// send data to browser
res.send(largeDataSet.join(""));
});
python.on("error", (err) => {
console.log(err);
res.sendStatus(500);
});
});

child_process.execFile slow to exit

I have a Node script that calls an external program (PluginManager.exe) this way:
const util = require('util');
const execFile = util.promisify(require('child_process').execFile);
const process = execFile('PluginManager.exe', ['/install']);
process
.then(({stdout, stderr}) => console.log('done', stdout, stderr))
.catch(e => console.log(e));
PluginManager.exe takes 8 seconds to execute. My problem is that the Node script keeps running for another 10 more seconds after the child process has exited. I know when PluginManager.exe finishes because I can see it disappear from the Windows Task Manager Process List.
What keeps the Node process running for so long and what can I do to make sure it exits as soon as the child process exits?
Maybe it's waiting on input and timing out after 10s?
Try closing stdin with .end() as mentioned in https://nodejs.org/api/child_process.html#child_process_subprocess_stdin
(in this usage, you'll need the original return value of execFile, so don't promisify, as per https://stackoverflow.com/a/30883005/1105015)
e.g.
const util = require('util');
const execFile = require('child_process').execFile;
const process = execFile(
'PluginManager.exe', ['/install'], (e, stdout, stderr) => {
if (e) {
console.log(e);
} else {
console.log('done', stdout, stderr));
}});
process.stdin.end();
Have you tried by adding the killSignal option set to something a bit more aggresive?
const process = execFile('PluginManager.exe', ['/install'], {killSignal: 'SIGKILL'});

Node.js child process isn't receiving stdin unless I close the stdin stream

I'm building a discord bot that wraps a terraria server in node.js so server users can restart the server and similar actions. I've managed to finish half the job, but I can't seem to create a command to execute commands on the terraria server. I've set it to write the command to the stdin of the child process and some basic debugging verifies that it does, but nothing apparently happens.
In the Node.js docs for child process stdin, it says "Note that if a child process waits to read all of its input, the child will not continue until this stream has been closed via end()." This seems likely to be the problem, as calling the end() function on it does actually send the command as expected. That said, it seems hard to believe that I'm unable to continuously send commands to stdin without having to close it.
Is this actually the problem, and if so what are my options for solving it? My code may be found below.
const discordjs = require("discord.js");
const child_process = require("child_process");
const tokens = require("./tokens");
const client = new discordjs.Client();
const terrariaServerPath = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Terraria\\TerrariaServer.exe"
const terrariaArgs = ['-port', '7777', "-maxplayers", "8", "-world", "test.wld"]
var child = child_process.spawn(terrariaServerPath, terrariaArgs);
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on('disconnect', () => {
client.destroy();
});
client.on('message', msg => {
if (msg.channel.name === 'terraria') {
var msgSplit = msg.content.split(" ");
if (msgSplit[0] === "!restart") {
child.kill();
child = child_process.spawn(terrariaServerPath, terrariaArgs);
registerStdio();
msg.reply("restarting server")
}
if (msgSplit[0] === "!exec") {
msg.reply(msgSplit[1]);
child.stdin.write(msgSplit[1] + "\n");
child.stdin.end();
}
}
});
client.login(tokens.discord_token);
var registerStdio = function () {
child.stdout.on('data', (data) => {
console.log(`${data}`);
});
child.stderr.on('data', (data) => {
console.error(`${data}`);
});
}
registerStdio();
I was able to solve the problem by using the library node-pty. As near as I can tell, the problem was that the child process was not reading the stdin itself and I was unable to flush it. Node-pty creates a virtual terminal object which can be written to instead of stdin. This object does not buffer writes and so any input is immediately sent to the program.

How to send input to child process created with spawn? nodejs

I'm running Windows 10, and I have a program, let's call it program, that can be run from the command line. When run, it responds to commands the user enters. The user enters a command, presses the return key, and the program prints a response. I did not make this program and do not have the source, so I cannot modify it.
I want to run this program from within Node.js, and have my Node.js program act as the user, sending it commands and getting the responses. I spawn my program like this:
var spawn = require('child_process').spawn;
var child = spawn('program');
child.stdout.on('data', function(data) {
console.log(`stdout: ${data}`);
});
Then I attempt to send it a command, for example, help.
child.stdin.write("help\n");
And nothing happens. If I manually run the program, type help, and press the return key, I get output. I want Node.js to run the program, send it input, and receive the output exactly as a human user would. I assumed that stdin.write() would send the program a command as if the user typed it in the console. However, as the program does not respond, I assume this is not the case. How can I send the program input?
I've seen many similar questions, but unfortunately the solutions their authors report as "working" did not work for me.
Sending input data to child process in node.js
I've seen this question and answer and tried everything in it with no success. I've tried ending the command with \r\n instead of \n. I've also tried adding the line child.stdin.end() after writing. Neither of these worked.
How to pass STDIN to node.js child process
This person, in their self-answer, says that they got theirs to work almost exactly as I'm doing it, but mine does not work.
Nodejs Child Process: write to stdin from an already initialised process
This person, in their self-answer, says they got it to work by writing their input to a file and then piping that file to stdin. This sounds overly complicated to send a simple string.
This worked for me, when running from Win10 CMD or Git Bash:
console.log('Running child process...');
const spawn = require('child_process').spawn;
const child = spawn('node');
// Also worked, from Git Bash:
//const child = spawn('cat');
child.stdout.on('data', (data) => {
console.log(`stdout: "${data}"`);
});
child.stdin.write("console.log('Hello!');\n");
child.stdin.end(); // EOF
child.on('close', (code) => {
console.log(`Child process exited with code ${code}.`);
});
Result:
D:\Martin\dev\node>node test11.js
Running child process...
stdout: "Hello!
"
Child process exited with code 0.
I also tried running aws configure like this, first it didn't work because I sent only a single line. But when sending four lines for the expected four input values, it worked.
Maybe your program expects special properties for stdin, like being a real terminal, and therefore doesn't take your input?
Or did you forget to send the EOF using child.stdin.end();? (If you remove that call from my example, the child waits for input forever.)
Here is what worked for me. I have used child_process exec to create a child process. Inside this child process Promise, I am handling the i/o part of the cmd given as parameter. Its' not perfect, but its working.
Sample function call where you dont need any human input.
executeCLI("cat ~/index.html");
Sample function call where you interact with aws cli. Here
executeCLI("aws configure --profile dev")
Code for custom executeCLI function.
var { exec } = require('child_process');
async function executeCLI(cmd) {
console.log("About to execute this: ", cmd);
var child = exec(cmd);
return new Promise((resolve, reject) => {
child.stdout.on('data', (data) => {
console.log(`${data}`);
process.stdin.pipe(child.stdin);
});
child.on('close', function (err, data) {
if (err) {
console.log("Error executing cmd: ", err);
reject(err);
} else {
// console.log("data:", data)
resolve(data);
}
});
});
}
Extract the user input code from browser and save that code into a file on your system using fs module. Let that file be 'program.cpp' and save the user data input in a text file.
As we can compile our c++ code on our terminal using g++ similarly we will be using child_process to access our system terminal and run user's code.
execFile can be used for executing our program
var { execFile } = require('child_process');
execFile("g++", ['program.cpp'], (err, stdout, stderr) => {
if (err) {
console.log("compilation error: ",err);
} else{
execFile ('./a.out' ,['<', 'input.txt'], {shell: true}, (err, stdout, stderr) => {
console.log("output: ", stdout);
})
}
})
In this code we simply require the child_process and uses its execFile function.
First we compile the code present in program.cpp, which creates a default a.out as output file
Then we pass the a.out file with input that is present in input.txt
Hence you can view the generated output in your terminal and pass that back to the user.
for more details you can check: Child Processes

Retrieve shell error output from child_process.spawn

I'm using child_process.spawn and need to capture the shell error that occurs when the command fails. According to this question, I should be able to do:
var child_process = require('child_process');
var python = child_process.spawn(
'python', ["script.py", "someParam"]
);
python.on('error', function(error) {
console.log("Error: bad command", error);
});
When I replace 'python', ["script.py", "someParam"] with banana, like in the linked question, it works, and the error is visible. But in my case, using python with arguments, the 'error' event is never called.
How can I capture shell errors from python?
According to the Node.js docs for the ChildProcess error event, it is only fired in a few situations:
The process could not be spawned, or
The process could not be killed, or
Sending a message to the child process failed for whatever reason.
To capture the shell error output, you can additionally listen to data events on the stdout and stderr of your spawned process:
python.stdout.on('data', function(data) {
console.log(data.toString());
});
python.stderr.on('data', function(data) {
console.error(data.toString());
});
To capture the shell error code, you can attach a listener to the exit event:
python.on('exit', function(code) {
console.log("Exited with code " + code);
});
Thread is a little bit old and all but I encountered this today while working on my test casing library. I realize that the accepted answer has a solution already, but, for me, it is not clearly explained. Anyway in case someone needs it here is what you need to do.
The thing I realized is that, while executing code, if python interpreter encounters an error, or should I say, if your code has an error in it, it will write it to standard error stream and exit. So what you need to do, in your Node.js code, is to listen to the stderr stream of the spawned process. In addition, all of the data passed to print() function is written to the 'stdout' stream of the process.
So here is an example code:
const { spawn } = require('child_process');
const proc = spawn('python',['main.py','-c']);
proc.stderr.on('data',(data)=>{
//Here data is of type buffer
console.log(data.toString())
})
proc.stdout('data',(data)=>{
//Also buffer
console.log(data.toString());
})
What happens clear should already be clear if you read the first part of my answer. One other thing you could do instead of writing data to the console, is redirect it to another stream, this could be really useful if you want to write output data to a file for example. This is how you could do it:
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
const outputFile = path.join(__dirname,'output.txt');
const errorFile = path.join(__dirname,'output.txt');
const outputStream = fs.createWriteStream(outputFile, {
encoding: "utf8",
autoClose: true
});
const outputStream = fs.createWriteStream(errorFile, {
encoding: "utf8",
autoClose: true
});
const proc = spawn('python',['main.py','-c']);
proc.stdout.pipe(outputStream);
proc.stderr.pipe(errorStream);
What is happening here is that, using pipe function we send all data from stdout and stderr of the process to the file streams. Also you do not have to worry about files existing, it will create them for you

Resources