Hi a Java newbie here.
I am currently building a Java application that executes multiple linux commands with Java Process builder.
I am planning to execute a shell scipt, and since it this shell script is an external program that takes about a second to fully execute, let the process sleep for a second and write the result into a txt file.
This is an external program and it must take in "q" to exit the program, so I finally need to type in q in the terminal.
I have gotten help on this community before and the code I constructed with that help is as follows.
public static void Linux(String fileName){
try {
File dir = new File("/usr/local/bin");
ProcessBuilder pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "./test_elanprv2.2.sh > \"$1\"; sleep 1; q", "_", fileName + ".txt"});
System.out.println("wrote text");
pb.directory(dir);
Process start = pb.start();
start.destroy();
}catch (Exception e){
e.printStackTrace();
System.out.println("failed to write text");
}
The process builder does create a txt file but it seems to be empty, and no matter how long I set the sleep to, the programs seems to instanly return the print statement.
It would really be appreciated if anyone could tell me what I am doing wrong.
Thank you in advance!!
As mentioned by #VGR, try using redirectOutput
public static void Linux(String fileName){
try {
File dir = new File("/usr/local/bin");
ProcessBuilder pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "./test_elanprv2.2.sh");
File output = new File("_", fileName + ".txt");
pb.redirectOutput(output);
System.out.println("wrote text");
pb.directory(dir);
Process start = pb.start();
start.destroy();
} catch (Exception e) {
e.printStackTrace();
System.out.println("failed to write text");
}
Most of your issues are due to a misunderstanding of how processes work. These concepts are not Java concepts; you would have the same issues in any other language.
First, you are destroying your process before it runs, and possibly before it even gets started. This is because pb.start() starts the process, and then you immediately destroy it without giving it a chance to complete.
You shouldn’t need to destroy the process at all. Just let it finish:
Process start = pb.start();
start.waitFor();
All processes have their own standard input and standard output. Again, this is not a Java concept; this has been a fundamental feature in Unix and Windows operating systems for a long time.
Normally, when a process prints information by writing it to its standard output. That is in fact what Java’s System.out.println does. In Unix shells (and in Windows), the > character redirects the standard output of a process to a file; the program still writes to its standard output, without ever knowing that the operating system is sending that output to a destination other than the terminal. Since it’s a fundamental operating system function, Java can do it for you:
ProcessBuilder pb =
new ProcessBuilder("/bin/bash", "-c", "./test_elanprv2.2.sh");
pb.redirectOutput(new File(fileName + ".txt"));
Similarly, when a process wants to take input, it normally does so by reading from its standard input. This is not the same as executing another command. When you do this:
./test_elanprv2.2.sh > "$1"; sleep 1; q
You are not sending q to the shell script. The above commands wait for the shell script to finish, then execute a sleep, then try to execute a program named q (which probably doesn’t exist).
Since the test_elanprv2.2.sh shell script probably accepts commands by reading its standard input, you want to send the q command to the standard input of that process:
ProcessBuilder pb =
new ProcessBuilder("/bin/bash", "-c", "./test_elanprv2.2.sh");
pb.redirectOutput(new File(fileName + ".txt"));
Process start = pb.start();
Thread.sleep(1000);
try (Writer commands = new OutputStreamWriter(start.getOutputStream())) {
commands.write("q\n");
}
// Caution: Call this AFTER writing commands. You don't want to write
// to the standard input of a process that has already finished!
start.waitFor();
Related
I am having trouble using the > operator to write to file in Process Builder. The actual process will run fine and let me parse using a InputStreamReader etc to parse the output. I want the process to be written to file using command line like >test.json for example.
Here is my code
try {
//process builder used to run a Python script
ProcessBuilder process = new ProcessBuilder("python",
System.getProperty("user.dir")+"\\createJson.py","--structure","cloc.csv",
"--weights","EntityEffort.csv",">","a.json");
process.directory(new File("c:\\users\\chris\\desktop\\test2"));
Process p = process.start();
} catch(Exception e) {
e.printStackTrace();
}
As #JimGarrison points out, > is interpreted by the shell. Here you are directly starting a process for createJson.py, without a shell.
In UNIX you could use ProcessBuilder to start a shell using:
process = new ProcessBuilder("bash", "someCommand", ">", "outputfile");
Something similar will probably work with Windows and cmd.exe.
However, it's not very good practice. It's an opportunity for command injection attacks. Calling external processes is a last-resort approach, and you should try and minimise what you do within those processes.
So you would be better off sticking with what you have, and handle the redirect to file in Java. The ProcessBuilder javadoc gives an example:
File log = new File("log");
processBuilder.redirectOutput(Redirect.appendTo(log));
I'm trying to execute a command from my Java web application (maven project with Tomcat 8 in Eclipse).
If I execute this command by the cmd line or by a .jar file there is no problem, but in Eclipse it doesn't work.
I tried ProcessBuilder class and Runtime.getRuntime().exec() but both throw the same "file not found exception" exception.
java.io.IOException: Cannot run program "mongodump": error=2, No such file or directory
The command i want to run is:
mongodump --db TweetsDB --collection Tweets --out ./TweetsDB_backup/
It seems that the runtime environments can't find the mongodump command (and/or the folder named in the command?)
Any suggestion?
Here it is my code:
public synchronized static void backup(){
Process p;
try {
String dateTimeInString = dateTimeToString(Calendar.getInstance());
System.out.println("backuppando "+dateTimeInString);
p = Runtime.getRuntime().exec("mongodump --db TweetsDB --collection Tweets --out ./backup/"); //override always the same db to save space
p.waitFor();
System.out.println("Backup effettuato correttamente :)");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
This is the "process builder" variant :
ProcessBuilder builder = new ProcessBuilder("mongodump", "--db", "TweetsDB","--collection", "Tweets", "--out" ,"./TweetsDB_backup/");
builder.redirectErrorStream(true); // equivalent of 2>&1
final Process process = builder.start();
InputStream es = process.getErrorStream();
InputStreamReader esr = new InputStreamReader(es);
BufferedReader br = new BufferedReader(esr);
String line;
while((line = br.readLine())!= null){
System.out.println(line);
}
Thanks,
Giulio
When you started the process with ProcessBuilder or Runtime.getRuntime().exec(), it would swallow any output from the process that is being started.
To see the real problem you need to get that output back.
With Runtime.exec() you need to use process.getErrorStream() and process.getInputStream() and set up threads to copy the output to somewhere you can see it.
with ProcessBuilder you can diurect the IO from the resulting process into the current (parent) processes using ProcessBuilder.inheritIO() before you call ProcessBuilder.start().
Use ProcessBuilder.inheritIO() to redirect the processes output to the servers log and you may find out what the error is.
try the following and see if it helps.
ProcessBuilder builder = new ProcessBuilder("mongodump", "--db", "TweetsDB","--collection", "Tweets", "--out" ,"./TweetsDB_backup/");
builder.inheritIO();
final Process process = builder.start();
Once you know what the error is you may be able to work out the solution.
I suspect you need to set some additional environment variables or something in the resulting process to get it to work.
I didn't try it yet, but maybe you can do it this way :
Mongo client = new MongoClient();
client.getDB("myDb").command("mongodump ...");
Throw No such file or directory is because Java can't get local path env. You should give absolute path of mongodump. Like this:
Runtime.getRuntime().exec("/usr/local/bin/mongodump --db TweetsDB --collection Tweets --out ./TweetsDB_backup/");
I have a Java Swing application which needs automatic restart. I have implemented it this way:
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
final File currentJar = new File(UltraCoinApp.class.getProtectionDomain().getCodeSource().getLocation().toURI());
/* is it a jar file? */
if(currentJar.getName().endsWith(".jar")) {
/* Build command: java -jar application.jar */
final ArrayList<String> command = new ArrayList<String>();
command.add(javaBin);
command.add("-jar");
command.add(currentJar.getPath());
final ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
}
System.exit(0);
After the restart the GUI is very slow, all elements respond slow. I suspect this might be because of logging output.
One important rule for ProcessBuilder is that you absolutely have to read the output of the child process or it will eventually hang.
That means you either have to redirect stdout while starting the process or you have to read the output in a loop in the parent process.
Another solution is to have an outside process (a script or executable) which runs your JAR in a loop. To terminate, call System.exit(1) and examine the exit code in the restart process.
If all that doesn't help, then you'll have to use a profiler to find out where the application is actually spending all the time.
I tried to run command line from Java code.
public void executeVcluto() throws IOException, InterruptedException {
String command = "cmd /c C:\\Users\\User\\Downloads\\program.exe C:\\Users\\User\\Downloads\\file.txt 5 >> C:\\Users\\User\\Downloads\\result.txt";
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
if (process.exitValue() == 0) {
System.out.println("Command exit successfully");
} else {
System.out.println("Command failed");
}
}
However, the file where output result should be written result.txt is not created. When I execute this command from cmd on windows the file is created and the result is written in it. I get Command exit successfully message. Could someone help me?
output redirection is shell feature, java Process does not understand that.
Some other alternatives are
1. create a single batch file with above lines and invoke it using ProcessBuilder/Runtime
2. Use ProcessBuilder and redirect output using output streams.
Example (it is for shell, will work for batch files too) is here
ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "C:\\Users\\User\\Downloads\\program.exe", "C:\\Users\\User\\Downloads\\file.txt" , "5");
builder.redirectOutput(new File("C:\\Users\\User\\Downloads\\result.txt"));
builder.redirectError(new File("C:\\Users\\User\\Downloads\\resulterr.txt"));
Process p = builder.start(); // throws IOException
(above is tweaked from Runtime's exec() method is not redirecting the output)
Try cmd.exe, including a path, if necessary.
You're creating an entirely new process, which is different than giving a command to a shell.
I'm making an update function for my project, it's working great, until i want it to restart, basically I download the new file and replace it with the old one, and then i want to run it again, now for some reason it doesn't wna run, and i don't get any error...
Here is the complete update class:
http://dl.dropbox.com/u/38414202/Update.txt
Here is the method i'm using to run my .jar file:
String currDir = new File("(CoN).jar").getAbsolutePath();
Process runManager = Runtime.getRuntime().exec("java -jar " + currDir);
It's not clear to me, why do you need to run the jar with a call to exec() . Given that you need to run the code in the .jar file from a Java program, you could simply run the main() method as defined in the jar's manifest, and capture its output - wherever that is.
Using exec() is OK when you need to call a program from the underlying operating system, but there are easier ways to do this if both the caller and the callee are Java programs.
Now, if your jar is gonna change dynamically and you need to update your program according to a new jar, there are mechanisms for reloading its contents, for instance take a look ath this other post.
The JavaDocs for the Process class specifically point out that if you don't capture the output stream of the Process and promptly read it that the process could halt. If this is the case, then you wouldn't see the process that you started run.
I think you have to capture the stream like this :
BufferedReader stdInput = new BufferedReader(new InputStreamReader(runManager.getInputStream()),8*1024);
BufferedReader stdError = new BufferedReader(new InputStreamReader(runManager.getErrorStream()));
// read the output from the command
String s = null;
System.out.println("Here is the standard output of the command:\n");
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
The exec function doesn't automatically lookup into the PATH to start a process, so you have to pass the complete path for the java binary.
You can do that by using the java.home system property, see this answer: ProcessBuilder - Start another process / JVM - HowTo?
No one here seemed to help me, so I went to ask my friend and I had it almost right. It abiously required the string to be an array.
solution:
String[] cmd = {"java", "-jar", currDir};
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e1) {
e1.printStackTrace();
}