opening a shell and interacting with its I/O in java
Your problem is that the standard input and output of the xterm process don't correspond to the actual shell that is visible in the terminal window. Rather than an xterm you may have more success running a shell process directly:
Process pr = new ProcessBuilder("sh").start();
Executing interactive shell commands in Java
I think that you even explained the answer into your question.
When you are running external application from java either using Runtime.exec()
or ProcessBuilder
you can access standard output and standard input streams. User input stream to read what external application writes and output stream to send commands to external application.
But be careful. Some applications will not get your commands sent from java. For example command ssh
in Unix system is designed to avoid its usage by non-human user (e.g. other application). It require to be executed from terminal for security reasons, so you cannot for example run ssh otherhost
and then send user/password from java. If you need this you have to run the command via other command line utility named expect
that simulates terminal and is driven by script.
Running an interactive shellscript using java
Read the process output stream and check for the input prompt, if you know the input prompt, then put the values to process inupt stream.
Otherwise you have no way to check.
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
BufferedReader b1 = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedWriter w1 = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
String line = "";
String outp = "";
while ((line = b1.readLine()) != null) {
if (line.equals("PLEASE INPUT THE VALUE:")) {
// output to stream
w1.write("42");
}
outp += line + "\n";
}
...
UPD: for your code it should be something like that
ProcessBuilder pb2=new ProcessBuilder("/home/ahijeet/sample1.sh","--ip="+formobj.getUpFile().getFileName(),"--seqs="+seqs);
script_exec = pb2.start();
OutputStream in = script_exec.getOutputStream();
InputStreamReader rd=new InputStreamReader(script_exec.getInputStream());
pb2.redirectError();
BufferedReader reader1 =new BufferedReader(new InputStreamReader(script_exec.getInputStream()));
StringBuffer out=new StringBuffer();
String output_line = "";
while ((output_line = reader1.readLine())!= null)
{
out=out.append(output_line+"/n");
System.out.println("val of output_line"+output_line);
//---> i need code here to check that whether script is prompting for taking input ,so i can pass it values using output stream
if (output_line.equals("PLEASE INPUT THE VALUE:")) {
// output to stream
in.write("42");
}
}
Command line terminal executing on process and input interaction from this process
To implement such use case I would personally use:
- An
Observable
object to notify my UI when a new line has been provided by the external process - An
Observable
object to which I add new lines provided by my UI - An
Observer
of #1 that will refresh the data of my UI - An
Observer
of #2 that will send the lines provided by my UI to my external process - A
Thread
that will check if a new line has been provided by my external process and if so it will provide those lines to #1
So as I don't have your full env, I will show you how it will work with mock objects:
First my fake external application that only does an Echo
of what he receives:
public class Echo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
String line = scanner.nextLine();
System.out.printf("echo > %s%n", line);
}
}
}
If this class receives foo
, it will print into the standard output stream echo > foo
Then my Observable
class
public class ObservableStream extends Observable {
private final Queue<String> lines = new ConcurrentLinkedQueue<>();
public void addLine(String line) {
lines.add(line);
setChanged();
notifyObservers();
}
public String nextLine() {
return lines.poll();
}
}
NB: The class ObservableStream
(as it is implemented so far) is meant to have only one Observer
no more which is enough according to your needs. Indeed is only used to decouple your UI from how the data is retrieved or published
Then finally the main code:
Process p = Runtime.getRuntime().exec(
new String[]{"java", "-cp", "/my/path/to/my/classes", "Echo"}
);
// The Observable object allowing to get the input lines from my external process
ObservableStream input = new ObservableStream();
// A mock observer that simply prints the lines provided by the external process
// but in your case you will update your text area instead
input.addObserver(
(o, arg) -> {
ObservableStream stream = (ObservableStream) o;
String line;
while ((line = stream.nextLine()) != null) {
System.out.printf("Line Received from the external process: %s%n", line);
}
}
);
// The thread that reads the standard output stream of the external process
// and put the lines into my variable input
new Thread(
() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()))
) {
String line;
while ((line = reader.readLine()) != null) {
input.addLine(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
).start();
// The Observable object allowing to send the input lines to my external process
ObservableStream output = new ObservableStream();
// Observer that simply sends to my external process line by line what we put in
// the variable output
PrintWriter writer = new PrintWriter(p.getOutputStream(), true);
output.addObserver(
(o, arg) -> {
ObservableStream stream = (ObservableStream) o;
String line;
while ((line = stream.nextLine()) != null) {
writer.println(line);
}
}
);
// A simple scanner used to send new messages to my external process
Scanner scanner = new Scanner(System.in);
while (true) {
output.addLine(scanner.nextLine());
}
If this code receives foo
, it will print into the standard output stream Line Received from the external process: echo > foo
(Blocking) interactive shell via ProcessBuilder
I decided to rewrite my binary to be non-interactive again. It turns out the expected performance gain was negligible so there was no more reason to keep it interactive and go through an increased implementation hassle.
Related Topics
Retrofit2 Android: Expected Begin_Array But Was Begin_Object at Line 1 Column 2 Path $
Firestore Query - Checking If Username Already Exists
Error: Unable to Load Installed Packages Just Now
Difference Between _Java_Options, Java_Tool_Options and Java_Opts
Setmaxresults for Spring-Data-JPA Annotation
How to Parse Output of New Date().Tostring()
Loading a Properties File from Java Package
How to Synchronize a Static Variable Among Threads Running Different Instances of a Class in Java
How to Avoid Using Scriptlets in My Jsp Page
Recyclerview Item Click Listener the Right Way
I Would Like to Set My Variables at the Top of My Class Instead of in the Method
Creating an Array of Objects in Java
Java: (String[])List.Toarray() Gives Classcastexception
Java Simpledateformat Always Returning January for Month
Singleton Design Pattern VS Singleton Beans in Spring Container
What's the Syntax to Import a Class in a Default Package in Java