Sending input to a program that accepts input via stdin - node.js

I have shell script that triggers after it receives an input from the stdin. It works as follows:
> ./ft/ft_0 model_32 -
>
> start_task_u
As you see above the executable ft_0 model_32 accepts input from the stdin which is denoted by - (where input entered is start_task_u). The user enters the input in the stdin and the program gets triggered. How could I do this with spawn in NodeJS?
I could start the process using spawn as follows:
spawn('./ft/ft_0',['model_32','-'])
but how could I send the input using the standard input after the program has started? I need to send start_task_u after the program has started and receive all the outputs that the shell script prints on the console.

Pass config option to inherit stdin and stdout from the parent process. Please find below
spawn('./ft/ft_0',['model_32','-'], {
stdio: [
0,
1,
]
})
Doc Link - https://nodejs.org/dist/latest-v10.x/docs/api/child_process.html#child_process_child_process_spawn_command_args_options

Related

Why doesn't the Linux redirection operator capture the output of my command?

Context: I have a program (go-sigma-rule-engine by Markus Kont) on my EC2 instance that runs against a logfile and produces some output to screen.
The command used to run this program is ./gsre/go-sigma-rule-engine run --rules-dir ./gsre/rules/ --sigma-input ./logs/exampleLog.json
The program produces output of the form:
INFO[2021-09-22T21:51:06Z] MATCH at offset 0 : [{[] Example Activity Found}]
INFO[2021-09-22T21:51:06Z] All workers exited, waiting on loggers to finish
INFO[2021-09-22T21:51:06Z] Stats logger done
INFO[2021-09-22T21:51:06Z] Done
Goal: I would like to capture this output and store it in a file.
Attempted Solution: I used the redirection operator to capture the output like so:
./gsre/go-sigma-rule-engine run --rules-dir ./gsre/rules/ --sigma-input ./logs/exampleLog.json > output.txt
Problem: The output.txt file is empty and didn't capture the output of the command invoking the rule engine.
Maybe the output you want to capture goes to standard error rather than standard output. Try using 2> instead of > to redirect stderr.

child.stdout.on works with read but not read -p

I'm trying to create a simple node API that would spawn a shell script and add user input from a POST call to that spawn. I created a controller called testController.js that would run a script called test.sh located in the same project
I was having a few problems writing the user input but thankfully this solution saved me.
So this really simple controller function ended up to be:
testController.js:
exports.create_task = function (req, res) {
var spawn = require("child_process").spawn;
var spawned = spawn("sh", ["/var/www/html/node_test_proj/test.sh"]);
spawned.stdout.on("data", function (data) {
console.log("In stdout");
spawned.stdin.write(req.body.name + "\n");
spawned.stdin.write(req.body.number + "\n");
});
res.send("posted");
};
My shell script would basically just take a name and number and export those details into a file:
test.sh:
#!/bin/bash
echo "Please input your name"
read name
echo "Please input your number"
read number
echo "Your name is $name and number $number" > knowingthis.txt;
Simple enough; does what it's supposed to and (given name abc and number 123) prints out:
your name is abc and number 123
However, to simplify things further I decide to replace the unnecessary echo statements with something simpler i.e read -p. Now my modified script becomes:
#!/bin/bash
read -p "Please input your name: " name;
read -p "Please input your number: " number;
echo "Your name is $name and number $number" > knowingthis.txt;
Lo and behold! Now when I spawn the script it no longer works; it doesn't even log the "In stdout" so that means that it's never really going in that clause, it simply exports the file with variables empty in the sentence, leaving the output to be:
your name is and number
I thought maybe there's something wrong with the script, so I ran it directly, but it was working fine. Why is it working with read and not read -p? Is there something I need to change in my function? Is it not a normal stdout stream but something else?
The man page section, or info page or website page, for builtin commands under read option -p says (emphasis added)
Display prompt, without a trailing newline, before attempting to read any input. The prompt is displayed only if input is coming from a terminal.
'coming from' means directly, i.e. only if file descriptor #0 (stdin) of the shell process is an open file which is a terminal and not a (redirected) disk file or pipe or socket. When nodejs spawns a child process, the child's stdin is a pipe from nodejs, and the child's stdout and stderr are pipes to nodejs; they (all) are not terminals.
OTOH echo writes to stdout unconditionally (regardless of what type of file it is).

Why is my stdin redirection ('<') not working with subprocess.Popen()?

I'm building a script in python, and one part of it needs to send an email with a file as the message body. Instead of sending the contents of the file, the script sends me the next character entered into the terminal e.g. if I enter c as a part of "cat", it doesn't put c into the terminal, but instead sends me an email with "c" as the body.
This is on CentOS 7.6.1810, with Python 3.5.6.
#!/usr/src/Python-3.5.6/python
import subprocess
import sys
import os
subprocess.Popen(["mail", "-s", "Test Subject", "myemail#myemail.com", "<", "/path/to/file.txt"], stdout=open('stdout', 'w'), stderr=open('errout', 'w'))
The contents of file.txt should be send as the body, but I just get the first letter of whatever the next thing I type is. "stdout" reads "EOT" after this, and nothing is printed to "errout". To be clear, I'm trying to invoke the command
mail -s "Test Subject" myemail#myemail.com < /path/to/file.txt
from inside of the script. This command works as expected outside of the Python script, but inside of it I run into the problem.
subprocess.Popen() executes your new process directly by default. So your code passes some additional arguments < and /path/to/file.txt to the mail executable, which will not yield the expected result.
Redirections like < on unix systems are handled by the shell, not by each individual executable. That's why you want subprocess.Popen() to run your mail command with all the arguments mail as well as the redirection < /path/to/file.txt in a shell instead.
You can do this with the shell=True parameter:
subprocess.Popen(["mail", "-s", "Test Subject", "myemail#myemail.com", "<", "/path/to/file.txt"], stdout=open('stdout', 'w'), stderr=open('errout', 'w'), shell=True)
Note that the shell is probably not necessary – you could have Popen open the file and connect mail's stdin to that descriptor, see anishsane's comment below.
Using a shell should be avoided especially if user data is being passed to the child process, as it would need to be sanitized properly to prevent command injection attacks.
See the Python 3 docs on subprocess.Popen.

How to get mongo shell output(three dot) for unterminated command

When type a unterminated command in a mongo shell, it will return three dots indicating need more input to complete this command like below:
> db.test.find(
... {
...
I am using nodejs child_process.spawn to create a mongo shell process and listen on its output. I can get the standard and error output from the mongo shell but I can't get the ... output. Below is my nodejs code:
const shell = spawn('mongo', params);
shell
.stdout
.on('data', (data) => {
winston.debug('get output ' + data);
});
shell
.stderr
.on('data', (data) => {
const output = data + '';
winston.error('get error output ', data);
});
I run below code to send command on the shell:
shell.stdin.write('db.test.find(');
I wander why I can't get the ... output on above method. Is it a special output?
EDIT1
I tried to use node-pty and pty.js. They can get the ... output but they mix the input and output data together. It is not possible to separate them.
I also tried to use stdbuf and unbuffer to disable buffer but it still doesn't work.
It seems that nodejs child_process doesn't work well with interactive command.
Your code doesn't include anything that writes to the stdin of your child process so I would be surprised if you got the ellipsis that indicates incomplete command when in fact you don't send any command at all - incomplete or otherwise.
That having been said, many command line utilities behave differently when they discover a real terminal connected to their stdin/stdout. E.g. git log will page the results when you run it directly but not when you pipe the results to some other command like git log | cat so this may also be the case here.
This can also have to do with the buffering - if your stream is line-buffered then you won't see any line that is not ended with a newline right away.
The real question is: do you see the > prompt? Do you send any command to the mongo shell?
Scritping interactive CLI tools can be tricky. E.g. see what I had to do to test a very simple interactive program here:
https://github.com/rsp/rsp-pjc-c01/blob/master/test-z05.sh#L8-L16
I had to create two named pipes, make sure that stdin, stderr and stdout are not buffered, and then use some other tricks to make it work. It is a shell script but it's just to show you an example.

How do I provide STDIN data to a J script run via jconsole.exe?

I want to run a J script, providing STDIN, and receive the output of the script with STDOUT.
I feel like I'm missing something blindingly obvious, but the help pages on using jconsole.exe are . . . terse.
My naive thought was that I could run the following in the cmd.exe shell to provide STDIN:
jconsole.exe script.ijs inputstring
While that works without the attempted STDIN:
C:\..\bin>jconsole.exe "C:\path\no-input-script.ijs"
success
C:\..\bin>
The no-input-script.ijs file is the following:
stdout 'success'
exit ''
I have the following script-with-input.ijs file:
input =: stdin ''
stdout 'input was ' , input
exit ''
When I run the following, the system hangs:
C:\..\bin>jconsole.exe "C:\path\script-with-input.ijs" xyz
When I then hit Ctrl+C, the script exits and I am left with the following:
C:\..\bin>jconsole.exe "C:\path\script-with-input.ijs" xyz
input was
C:\..\bin>
stdin reads input from STDIN until EOF (usually in *nix ^D). So your 'script-with-input.ijs' waits for user input or a pipe.
c:>jconsole.exe "script-with-input.ijs" hello
this is user input
^D
input was this is user input
What you, instead, are trying to do is read the arguments of the command. Those are stored in ARGV:
NB. script-with-input.ijs
input =: ARGV
echo input
exit''
Then:
c:>jconsole.exe "script-with-input.ijs" hello
┌────────────┬─────────────────────┬─────┐
│jconsole.exe│script-with-input.ijs│hello│
└────────────┴─────────────────────┴─────┘

Resources