How to execute a bash script from java program - java

I would like my Java program to execute a bash script and return the output back to Java. The trick is my script starts some sort of 'interactive session' and I suppose that is why my Java application freezes (Enters an infinite loop I suppose). Here is the code I use to execute the script, I use ProcessBuilder in order to do that. I also tried
Runtime.getRuntime().exec(PathToScript);
It doesn't work either.
public class test1 {
public static void main(String a[]) throws InterruptedException, IOException {
List<String> commands = new ArrayList<String>();
List<String> commands1 = new ArrayList<String>();
commands.add("/Path/To/Script/skrypt3.sh");
commands.add("> /dev/ttys002");
ProcessBuilder pb = new ProcessBuilder(commands);
pb.redirectErrorStream(true);
try {
Process prs = pb.start();
Thread inThread = new Thread(new In(prs.getInputStream()));
inThread.start();
Thread.sleep(1000);
OutputStream writeTo = prs.getOutputStream();
writeTo.write("oops\n".getBytes());
writeTo.flush();
writeTo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class In implements Runnable {
private InputStream is;
public In(InputStream is) {
this.is = is;
}
#Override
public void run() {
try {
byte[] b = new byte[1024];
int size = 0;
while ((size = is.read(b)) != -1) {
System.out.println(new String(b));
}
is.close();
} catch (IOException ex) {
Logger.getLogger(In.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
And here is the script I try to execute. It works like a charm when I run it directly from terminal.
#!/bin/bash
drozer console connect << EOF > /dev/ttys002
permissions
run app.package.info -a com.mwr.example.sieve
exit
EOF

You should not be trying to add redirect instructions as part of the command name:
commands.add("/Path/To/Script/skrypt3.sh");
commands.add("> /dev/ttys002");
ProcessBuilder pb = new ProcessBuilder(commands);
Instead, use the redirectOutput method, something like this:
tty = new File("/dev/ttys002");
ProcessBuilder pb = new ProcessBuilder("/Path/To/Script/skrypt3.sh")
.redirectOutput(ProcessBuilder.Redirect.appendTo(tty))
.redirectError(ProcessBuilder.Redirect.appendTo(tty))
.start();
Though, it appears your bash script is already handling the redirection so not sure you need to do that in Java.
See this answer for more info.

Related

Java Thread doesn't live as long as the main program does

What I'm trying to accomplish here is to instantiate via Apache Commons Exec an instance of a second .jar apart from the main one which throws this second Java program.
What this second .jar does is basically send bytes to the stdout. This is the code that launches this program.
private void runJar(PipedOutputStream output) throws IOException {
DefaultExecutor executor = new DefaultExecutor();
CommandLine commandLine;
String executeMe = "java -jar myjar.jar";
commandLine = CommandLine.parse(executeMe);
executor.setStreamHandler(new PumpStreamHandler(output, null));
executor.execute(commandLine, new DefaultExecuteResultHandler());
}
But so far I couldn't find a way to execute this without blocking the normal flow of the normal program with the library, so what I did as a workaround is to create Thread, like this...
Thread t3 = new Thread() {
public void run() {
try {
runJar(output);
} catch (IOException e) {
e.printStackTrace();
}
}
};
t3.start();
I've tried launching the commands via the cmd and everything works properly, but in this case when I run everything inside Java the Thread seems to stop after several seconds.
Am I missing something when I create a new Thread so it can live as long as the main program does?
UPDATE:
A runnable example from the first .jar can be this code...
public class SecondApp {
public static void main(String[] args) throws ClassNotFoundException, IOException, InterruptedException {
File in = new File(args[0]);
try (InputStream input = new FileInputStream(in)) {
int bytesRead, CHUNK_SIZE = 4096;
byte[] data = new byte[CHUNK_SIZE];
while (true) {
bytesRead = input.read(data, 0, CHUNK_SIZE);
if (bytesRead > 0) {
System.out.write(data, 0, bytesRead);
System.out.flush();
} else if (bytesRead == -1) {
System.exit(0);
}
}
}
}
It basically spits out bytes to the stdout. The third app is just an external one (ffmpeg), which receives those bytes from this second .jar via a PipedInputStream, like this...
PipedOutputStream output = new PipedOutputStream();
PipedInputStream input = new PipedInputStream();
output.connect(input);
In order to debug and know the Thread status succesfully, I've changed the asynchronous execution for a synchronous one, and now I get that both threads are in RUNNABLE state and are ALIVE.
#Override
public void run() {
try {
DefaultExecutor executor = new DefaultExecutor();
CommandLine commandLine;
String executeMe = "java -jar myjar.jar";
commandLine = CommandLine.parse(executeMe);
executor.setStreamHandler(new PumpStreamHandler(output, null));
executor.execute(commandLine); //, new DefaultExecuteResultHandler());
} catch (ExecuteException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
As #matt stated, it may be a problem of the output from the FFMPEG thread not being consumed.
executor.setStreamHandler(
new PumpStreamHandler(
null, // stdout
null, // stderr
input)); // stdin
That was the previous line from the FFMPEG executor, now changed by a NullOutputStream (the one from Apache Commons Exec).
executor.setStreamHandler(
new PumpStreamHandler(
new NullOutputStream(), // stdout
null, // stder
input)); // stdin
After those changes, the stream seems to stop after few seconds, so it may be the reason #matt said, the stream isn't being consumed and gets full. Why isn't it working with a NullOutputStream()?

.java file to run another .java (frames) through Process

Real Goal: create a program that calls other programs(lab exercises)
Current goal: Make Main.java run Lab4 a GUI program (Lab4Ans201506159.java - the filename)
Lab4Form and Lab4Intro are forms
here is the Main.java code
public class Main {
public static void main(String[] args) throws IOException {
// TODO code application logic here
Process p,p2,p3,p4;
p = Runtime.getRuntime().exec("javac Lab4Ans201506159.java");
//p3 = Runtime.getRuntime().exec("javac Lab4Ans201506159Form.java");
//p4 = Runtime.getRuntime().exec("javac Lab4Ans201506159Intro.java");
p2 = Runtime.getRuntime().exec("java Lab4Ans201506159");
//p2 = Runtime.getRuntime().exec("Lab4Ans201506159");
}
and here is the Lab4 code
Lab4Form and Lab4Intro are Frames
what Lab4 is trying to do displaying Lab4Intro, and when it is closed, Lab4Form would be visible
public class Lab4Ans201506159 {
public static void main(String[] args) throws InterruptedException {
Lab4Ans201506159Intro intro = new Lab4Ans201506159Intro();
intro.setLocationRelativeTo(null);
intro.setVisible(true);
Thread.sleep(2000);
//Lab4Ans201506159Form form = new Lab4Ans201506159Form();
while(intro.isActive())
{
}
if(intro.isActive() == false){
Lab4Ans201506159Form form = new Lab4Ans201506159Form();
form.setLocationRelativeTo(null);
form.setVisible(true);
}
}
Problem: Running Main.java will result to a "BUILD SUCCESSFUL" in the compiler but no GUI is displayed. I need answers why it does not display or work.
I suspect only the first Process is executed, in order to be sure, have you already tried to redirect the output of Runtime.exec to the standard output
something like that:
public static void main(String[] args) throws Exception {
System.setOut(new PrintStream(new FileOutputStream("log.txt")));
System.out.println("Init...");
try {
String line;
Process p = Runtime.getRuntime().exec( "javac Lab4Ans201506159.java" );
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()) );
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
}
Thread.sleep(1000);
Process p2 = Runtime.getRuntime().exec("java Lab4Ans201506159" );
BufferedReader in2 = new BufferedReader(
new InputStreamReader(p2.getInputStream()) );
while ((line = in2.readLine()) != null) {
System.out.println(line);
}
in2.close();
}
catch (Throwable e) {
e.printStacktrace();
}
}
like that you can verify what is going wrong...
good luck
I managed to finish my end goal which is to open Lab4. I sort of took a different method though. I suspect one of the reasons why it does not work is because my classpath must have been wrong. I could say that because I can't compile (javac) in CMD Prompt. So I fixed that, then I 'clean and build' (using Netbeans) the project(lab4,intro,form). After that, in the last line of the compiler there will be a line like "java -jar C:\sdfsafs\blablabal". That was the line I used inside runtime.exec() and it finally worked.
public static void main(String[] args) throws Exception {
try {
runProcess("java -jar \"C:\\Users\\Aldrin\\Desktop\\201506159AnsLab4\\dist\\201506159AnsLab4.jar\"");
//runProcess("dir");
//runProcess("java Lab4Ans201506159");
} catch (Exception e) {
e.printStackTrace();
}
}
I still have not answered why the original code does not work though.

getRuntime().exec() does nothing

I want a java program to execute the following shell command:
apktool.jar d /path/to/my/app.apk
This command perfectly works when executing it directly on command line.
Java Code:
public static void main(String[] args) {
String command = "apktool d /path/to/my/app.apk";
try {
Runtime.getRuntime().exec(command);
} catch (IOException e1) {
e1.printStackTrace();
}
}
There is no error, no exception. Nothing happens and i have the impression that I already searched the entire internet for a solution. Does anybody know what I am doing wrong? A simple command like
mkdir /path/to/a/new/folder
works without problems.
I tried the same using ProcessBuilder:
try {
Process process = new ProcessBuilder(command).start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
This time i only get "Cannot run program "apktool d /path/to/my/app.apk, No such file or directory". I can't even run the mkdir command.
You need to call the jar with java.exe, and you're not doing that. Also you need to trap the input and error streams from the process, something you can't do the way you're running this. Use ProcessBuilder instead, get your streams and then run the process.
For example (and I can only do a Windows example),
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class ProcessEg {
private static Process p;
public static void main(String[] args) {
String[] commands = {"cmd", "/c", "dir"};
ProcessBuilder pBuilder = new ProcessBuilder(commands);
pBuilder.redirectErrorStream();
try {
p = pBuilder.start();
InputStream in = p.getInputStream();
final Scanner scanner = new Scanner(in);
new Thread(new Runnable() {
public void run() {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
try {
int result = p.waitFor();
p.destroy();
System.out.println("exit result: " + result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Try doing it like this:
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec("./path/apktool d /path/to/my/app.apk");
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
system.out.println(output.toString());
Creating first a process allows you to wait for a response and reads the output of the execution of your process.
If something is failing while running your shell command, you will have the error printed at the end.
Also, make sure your java program can access your shell script, or better provide the full path to it like:
./path/to/shell/apktool d /path/to/my/app.apk

Execute java file with Runtime.getRuntime().exec()

This code will execute an external exe application.
private void clientDataActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try {
Runtime.getRuntime().exec("C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe");
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
What if I want to execute external java file? Is it possible? For example like this command:
Runtime.getRuntime().exec("cmd.exe /C start cd \"C:\Users\sg552\Desktop\ java testfile");
The code does not work from java and cmd prompt. How to solve this?
First, you command line looks wrong. A execution command is not like a batch file, it won't execute a series of commands, but will execute a single command.
From the looks of things, you are trying to change the working directory of the command to be executed. A simpler solution would be to use ProcessBuilder, which will allow you to specify the starting directory for the given command...
For example...
try {
ProcessBuilder pb = new ProcessBuilder("java.exe", "testfile");
pb.directory(new File("C:\Users\sg552\Desktop"));
pb.redirectError();
Process p = pb.start();
InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
consumer.start();
p.waitFor();
consumer.join();
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
}
//...
public class InputStreamConsumer extends Thread {
private InputStream is;
private IOException exp;
public InputStreamConsumer(InputStream is) {
this.is = is;
}
#Override
public void run() {
int in = -1;
try {
while ((in = is.read()) != -1) {
System.out.println((char)in);
}
} catch (IOException ex) {
ex.printStackTrace();
exp = ex;
}
}
public IOException getException() {
return exp;
}
}
ProcessBuilder also makes it easier to deal with commands that might contain spaces in them, without all the messing about with escaping the quotes...

Is is possible to direct windows console output to a GUI?

I'm working on a project and I would like to execute programs in the windows console or a linux terminal.
Instead of launching a new console and working the program in it I want to do something like the following:
rt.exec("cmd.exe /c start cmd.exe /k ruby rubycode.rb");
From this point on I want the user to be able to work with the program from the GUI/my program. The idea in my min is starting cmd in silent mode where it is not visible and latching on to it. Then redirecting the console output to the GUI and letting the user input data to the console through the GUI.
A similar concept is what most IDEs like jgrasp do. When you run a program you interface with it though their own command prompt.
How is this done? Iv'e tried grabbing the IOStreams from the process and trying to atleast print what the console outputs but no luck.
Here is an example:
public class ProcessTest {
private Process p;
private BufferedReader reader;
private BufferedWriter writer;
public void start() throws IOException {
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "dir");
pb.directory(new File("./"));
this.p = pb.start();
this.reader = new BufferedReader(new InputStreamReader(this.p.getInputStream()));
this.writer = new BufferedWriter(new OutputStreamWriter(this.p.getOutputStream()));
new Read(this.reader).start();
}
public boolean writeToConsole(String s) throws IOException {
if (p == null)
return false;
this.writer.write(s + "\n");
this.writer.flush();
return true;
}
public class Read extends Thread {
private BufferedReader reader;
public Read(BufferedReader reader) {
this.reader = reader;
}
public void run() {
try {
String line;
while ((line = this.reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
new ProcessTest().start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
With the writeToConsole method you can write any string to the programm that you executed.

Categories

Resources