Reading from BufferedReader - java

I am trying to execute commands on the terminal. using
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();
OutputStream stdin = process.getOutputStream();
InputStream stdout = process.getInputStream();
reader = new BufferedReader(new InputStreamReader(stdout));
writer = new BufferedWriter(new OutputStreamWriter(stdin));
I am going to use this reader and and writer to continuously communicate with the process.
I'm using the following loop to read
while ((line = reader.readLine()) != null) {
System.out.println("line);
}
ISSUE: The problem here is that, when the reader starts reading from the buffer its forever in the while loop. It never exits.
I tried to put the reading in a thread
public void run() {
try {
String line;
outputText = "";
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException ex) {
Logger.getLogger(ThreadReader.class.getName()).log(Level.SEVERE, null, ex);
}
}
But now i have no control on when the reader starts and finishes reading.
GOAL: I need to execute a command and read the output and then the next command.

Your Java program is communicating with an external interactive process. It has only the process's output and error streams to work with to determine how to proceed from any given point. If you want it to recognize subdivisions of the output, such as responses to individual commands, then you need to teach it what the boundaries of those subdivisions look like.
There are any number of ways you might approach the problem. For example, if the external program emits a prompt when it is ready to accept a new command, then it seems natural to watch for that prompt. Alternatively, perhaps you can tweak the input to the program so as to cause it to produce a recognizable marker at the end of each command's output.
Do also consider that this is a solved problem (many times over). The canonical utility for general-purpose scripting of interactive programs is a Tcl program called "Expect". It has inspired work-alikes in many languages, including many in Java. Google gives me three distinct examples among the first five hits, but I have no specific experience with any of them so I make no recommendation.

Related

Output from Process.getInputStream() buffered too long

I have a Java program that uses Process Builder to start an external program.
I have a Process Builder with the command, and I use the code below to get the program going.
What I want to do is to print out the output just as the external program would have done, and I try to do this with the following code:
pb.redirectErrorStream(true);
p = pb.start();
InputStream is = p.getInputStream();
InputStreamReader isr = new inputStreamReader(is);
BufferedReader br = new BufferedReader(isr, 32);
while((line = br.readLine()) != null){
System.out.println(line);
}
The problem is that my program is all silent until the external program ends, when suddenly all output comes at once. Or it might not have benn silent if there had been more output, but I want it to produce the output as it comes, ie unbuffered.
The (to me) obvious problem is the BufferedReader and the size of its buffer, but the thing is that I've tried to have a really small buffer, to no avail.
I've also tried to avoid the BufferedReader, and work with the InputStreamReader directly instead, but thet bahaves the same way.
Anyone here that can understand my problem, and perhaps have a solution?
This is caused by buffering in the executed program, not by Java. Nothing you can do about it from the Java end.
This could help
Get Java Runtime Process running in background
run a thread when reading the output of an external application

interacting with console app using java

i want to open an external app using java.
Process p = Runtime.getRuntime().exec("/Users/kausar/myApp");
this runs the process as i can see in activity monitor.
Now the file i run is actually console app which then takes commands and gives response based on those commands.
for example if i go to terminal and put the same
Kausars-MacBook-Air:~ kausar$ /Users/kausar/myApp
myApp>
Now i can give commands to app as for example
myApp> SHOW 'Hi There'
These are commands taken as keyboard input in the console app, these are not parameters. I have seen different approaches with parameters. I tried the following as well but couldnt get it to work.
String res;
String cmnd = "SHOW \'Hi There\'";
OutputStream stdin = null;
InputStream stdout = null;
stdout = p.getInputStream();
stdin = p.getOutputStream();
stdin.write(cmnd.getBytes());
stdin.flush();
p.waitFor();
BufferedReader input = new BufferedReader(
new InputStreamReader(stdout));
while ((res = input.readLine()) != null) {
System.out.println(res)
}
input.close();
p.destroy();
Its displaying nothing while the same procedure with "/bin/bash -c ls" works just fine.
please help!
Of hand I would say the problem is with p.*wait*For()
Exactly what object and when to usee notify() or notifyAll() call to wake up the object thread would be something like on stdout and maybe a restructure of the process.
note: an interesting feature is the class field in BufferedReader called "lock", the api docs do mention some way of structuring your program so it can be notified.

Runtime.getRuntime exec

I have created a class to handle root commands in an android app, which is working just fine mostly. I create the process with Runtime.getRuntime().exec("su") and then enter commands using DataOutputStream and get the data from the commands using DataInputStream and the deprecated readLine. (I have tried using BufferedReader instead, but no difference to the issue).
My problem is that the app will hang if the command produces an error. F.eks. if I execute the command "[ -f /test ] && md5sum /test" || echo 0" I will have no problems. However, if I execute "md5sum /test" and the file does not exist, I will have to force-close the app as it will get nowhere. In this example the solution is of cause just to check for the file like in the first example, but not every situation is this simple. Issues can happen, and when they do, people should not have to force-close applications.
Is there a way to fix this issue?
Your app is likely hanging because you probably aren't processing stderr/stdout generated by the spawned process. The reason behind this is that spawned processes have very small output buffers (usually only a few kilobytes). Once those buffers are full, that process will hang until its buffers free up enough space for it to continue writing output text. I suspect you're having problems when running that second command because that second command is failing and generating lots of console output. Your child process runs out of buffer space and then tries to block until more space becomes available, which never happens.
Runtime.getRuntime().exec() returns an instance of Process. Process objects have an accessor method (I believe it's getInputStream()) that allow you to consume its stdout. You also need to do the same with getErrorStream(). Typically, I get the InputStream, then have a separate thread continually consume data from that InputStream until it's closed. You don't need to do anything with the data per se, just read it. This will tell the underlying process that it can clear its output buffers, hopefully before they ever become full (thus, causing the Process to block).
Also, I'm not 100% familiar with Android, but in plain ol' java, it's better to use ProcessBuilder to spawn child Process instances. This is because ProcessBuilder lets you combine the child's stderr AND stdout in the same stream, which lets you consume both within a single thread by just reading data from the stream returned by process.getInputStream().
Okay I have looked at ProcessBuilder (Which exists for android as well). But I still run into to a problems.
while ((line = buffer.readLine()) != null) {
data = line;
}
if (data != null && data.contains("uid=0")) {
return true;
}
This will provide the same issue as before. It get's nowhere. However, if I change the code to
while ((line = buffer.readLine()) != null) {
if (data != null && data.contains("uid=0")) {
return true;
}
}
This will work fine. The process will come to an end, and it will return true. The problem is that in order for me to use this, I would have to know exacly what to expect as the return data (Which I do in this test method). So this cannot be the solution.
Here are the entire testing method
try {
ProcessBuilder builder = new ProcessBuilder("su");
builder.redirectErrorStream(true);
Process process = builder.start();
DataOutputStream output = new DataOutputStream(process.getOutputStream());
InputStreamReader reader = new InputStreamReader(process.getInputStream());
BufferedReader buffer = new BufferedReader(reader);
output.writeBytes("id\n");
output.flush();
String line = null;
String data = null;
while ((line = buffer.readLine()) != null) {
data = line;
}
if (data != null && data.contains("uid=0")) {
return true;
}
} catch(Throwable e) {
Log.d(TAG, "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage());
}
return false;

shell process java synchronization

I want to run a shell script from a java program. This shell script invokes a system library which needs a big file as resource.
My java program calls this script for every word in a document. If I call this script again and again using Runtime.exec() the time taken is very high since the resource loading takes lot of time.
To overcome this I thought of writing the shell script as follows (to make it run continuously in background ):
count=0
while count -lt 10 ; do
read WORD
//execute command on this line
done
I need retrieve the output of the command in my java program and process it further.
How should I code the I/O operations for achieving this task?
I have tried writing words in to the process's output stream and reading back output from process's input stream. But this does not work and throws a broken pipe exception.
try {
parseResult = Runtime.getRuntime().exec(parseCommand);
parsingResultsReader = new BufferedReader(new InputStreamReader (parseResult.getInputStream()));
errorReader = new BufferedReader(new InputStreamReader (parseResult.getErrorStream()));
parseResultsWriter = new BufferedWriter(new OutputStreamWriter((parseResult.getOutputStream())));
} catch (IOException e) {
e.printStackTrace();
}
parseResultsWriter.write(word);
parseResultsWriter.flush();
while ((line = parsingResultsReader.readLine()) != null) {
// capture output in list here
}
Kindly help with this issue
//execute command on this line
Is this command a separate program? Then it will be launched for every word, so you'll get rid of only shell process which is lightweight anyway.
You have to learn how to run the heavyweight command for many words at once.

When using java runtim.exec to ssh, how do I get console output of that remote machine?

Say we have this method to make an ssh to another machine. How would I get the output from that machines terminals back to the host machine
public void getSSHreply()
{
Process p;
// Set up the arguments for ProcessBuilder
String[] cmd =
{
"/usr/bin/ssh",
"someRemoteMachine", //This machine will authenticate with keys, hence no pw needed
"./myprog",
};
try
{
p = Runtime.getRuntime().exec(cmd);
//How would I redirect stdout back to host machine?
StringBuffer s = new StringBuffer();
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while (input.readLine() != null)
{
s.append(input.readLine() + "\n");
}
System.out.println(s.toString());
}
catch (IOException e)
{
System.err.println("Failed to read & or start ");
}
}
The Process object has methods for getting the STDOUT, STDERR, and STDIN streams. (ex. getOutputStream()).
You may want to look into commons-exec for more convenient ways to launch and manager external programs, which has tools like StreamPumper to redirect data.
Unfortunately you can't. The simplest way is probably to read p's inputStream and errorStream (normally in two separate threads).
I believe your immediate problem is because you are using a BufferedReader - so when SSH displays the "Password: " prompt (which doesn't have a terminating line feed) Bufferedreader won't be returning anything to your input.readLine() call.
The easiest thing is to read the input a single character at a time (though not the most efficient, of course).
You'll also probably want to read the stderr stream as well, which is why you might want a couple of threads.

Categories

Resources