trying to execute an script, using this piece of code:
String command = "./myScript.sh";
pb = new ProcessBuilder(command, param1, param2);
pb.directory(directory);
pb.start();
I am not getting any kind of error, but neither the supposed results. Anyway, I tryed to run the same command, direclty in the terminal, and everything working correctly.
Am I missing something??
Thanks in advance
When you start a process (pb.start()) you get back a Process instance. If your script reads input or writes output to stdout or stderr you need to handle this on separate threads using Process.getInputStream(), ...getOutputStream() and getErrorStream(). If you don't do this the process can hang. You also should call Process.waitFor() and then Process.exitValue() to get the return status of the process. If it's a negative number then the system was unable to launch your script.
EDIT: Here is a short simplified example. This is a toy only and will work reliably ONLY under the following conditions:
The script does not require any input
The script does not produce a large amount of output on both stdout and stderr. If it does, then since the program reads all of stdout before stderr, the stderr buffer may fill up and block the process from completing. In a 'real' implementation you would read stdout and stderr in separate threads (hint, wrap the loadStream() method in a class that implements Runnable).
public class PBTest
{
public static void main(String[] args) throws Exception
{
ProcessBuilder pb = new ProcessBuilder("sc","query","wuauserv");
Process p = pb.start();
String output = loadStream(p.getInputStream());
String error = loadStream(p.getErrorStream());
int rc = p.waitFor();
System.out.println("Process ended with rc=" + rc);
System.out.println("\nStandard Output:\n");
System.out.println(output);
System.out.println("\nStandard Error:\n");
System.out.println(error);
}
private static String loadStream(InputStream s) throws Exception
{
BufferedReader br = new BufferedReader(new InputStreamReader(s));
StringBuilder sb = new StringBuilder();
String line;
while((line=br.readLine()) != null)
sb.append(line).append("\n");
return sb.toString();
}
}
The problem was not on the way I called the script, which was right.
But it was inside the script. At first it was:
#!/bin/bash
inputFolder=$1
outputFolder=$2
cd $inputFolder
for file in `ls ` ; do
ffmpeg -i $inputFolder/$file -ar 22050 $outputFolder/$file.mp4
done
But I got ffmpeg command not found, so I changed it to:
#!/bin/bash
inputFolder=$1
outputFolder=$2
cd $inputFolder
for file in `ls ` ; do
/usr/local/bin/ffmpeg -i $inputFolder/$file -ar 22050 $outputFolder/$file.mp4
done
with the hole path. But I have still doubts, why this is necessary, if I have ffmpeg in my path and I cand execute in console direclty form any directory??
If someone can give me an answer, it will be welcome :)
Related
I am calling a bash scrip script from Java.
The script does the following:
cat /home/user/Downloads/bigtextfile.txt | grep 'hello'
This particular command when run command line takes about 1 second to complete on the text file which is 150MB.
When calling the bash script via Java using the following call:
command = "sh /home/user/bashfiletocall"
p = Runtime.getRuntime().exec(command);
The time to complete takes so long I don't wait.
Am I doing something very wrong and if not can you explain the reason for the huge lack in performance?
NOTE: I was running it in Netbeans and this seems to be the problem .. when I ran the file command line it was quick. The performance between execution in netbeans and command line is huge.
Many thanks.
private String executeCommand(String command) {
StringBuilder output = new StringBuilder();
BufferedReader reader = null;
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
After starting your process you need start reading from the input stream. Otherwise the buffers are running full and p.waitFor() waits forever.
Javadoc of the Process class:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
I am running the following using Apache exec lib.
wmic LOGICALDISK GET Name,ProviderName /FORMAT
This command returns a listing of all the mapped drives and their mapping. When I run it from the command line, it works great. When I run it from within java, it returns the first 2 drives and 1 of the drives listed in the middle.
I've piped the stream to stdout, used stream gobbler, etc. I have several other commands I run that work fine and I read the streams without problem. I'm stumped. Any ideas? Encoding possibly? I've never had this problem before.
Oh, I've also run with ProcessBuilder, Runtime.exec, and DefaultExecutor. Same results throughout.
Thanks.
stdbuf -o0 wmic LOGICALDISK GET Name,ProviderName /FORMAT | cat
download stdbuf from: http://www.inreto.de/ffp/0.7/arm/packages/coreutils-8.14-arm-1.txz
or maybe:
public static String execCmd(String cmd) throws java.io.IOException {
Process proc = Runtime.getRuntime().exec(cmd);
java.io.InputStream is = proc.getInputStream();
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
String val = "";
if (s.hasNext()) {
val = s.next();
}
else {
val = "";
}
return val;
}
from:https://stackoverflow.com/a/20624914/264181
I have the following windows batch file (run.bat):
#echo off
echo hello batch file to sysout
And the following java code, which runs the batch files and redirects output to a file:
public static void main(String[] args) throws IOException {
System.out.println("Current java version is: " + System.getProperty("java.version"));
ProcessBuilder pb =
new ProcessBuilder("cmd.exe", "/c",
"run.bat"
,">>", "stdout.txt","2>>", "stderr.txt"
);
System.out.println("Command is: " + pb.command());
Process proc = pb.start();
InputStream in = proc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitValue = proc.exitValue();
System.out.println("Exit value: " + exitValue);
}
On JDKs up to and including JDK6u43 I get the following output:
Current java version is: 1.6.0_29
Command is: [cmd.exe, /c, run.bat, >>, stdout.txt, 2>>, stderr.txt]
Exit value: 0
and the script output is written to the file.
As of JDK 6u45 and 7, I get the following output:
Current java version is: 1.6.0_45
Command is: [cmd.exe, /c, run.bat, >>, stdout.txt, 2>>, stderr.txt]
hello batch file to sysout
Exit value: 0
And nothing is written to the output file.
This may or may not be related to the changes made in Runtime.exec() , described at: http://www.oracle.com/technetwork/java/javase/6u45-relnotes-1932876.html
What is the correct way of starting a process on Windows with output redirected to files?
Note: In a real world scenario, the command to execute may include parameters with spaces, as in:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c",
"run.bat", "Some Input With Spaces",
">>", "stdout.txt","2>>", "stderr.txt");
This is the simplest method i found on http://tamanmohamed.blogspot.in/2012/06/jdk7-processbuilder-and-how-redirecting.html
File output = new File("C:/PBExample/ProcessLog.txt");
ProcessBuilder pb = new ProcessBuilder("cmd");
pb.redirectOutput(output);
Several suggestions here:
Does the input with the spaces need to be treated as single String (with spaces),or id it in actual several inputs? If the first Option is the case I would suggest to quote it for the windows runtime:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c",
"run.bat", "\"Some Input With Spaces\"",
">>", "stdout.txt","2>>", "stderr.txt");
Instead of redirecting the input to stdout.txt and stderr.txt using the shell, why not do it using Java using getOutputStream() and getErrorStream()? Here is an example using Guava's IO package. Of course you may want to have those in separate threads, you need proper exception handling, etc.
InputStream stdout = new BufferedInputStream(proc.getInputStream());
FileOutputStream stdoutFile = new FileOutputStream("stdout.txt");
ByteStreams.copy(stdout, stdoutFile);
InputStream stderr = new BufferedInputStream(proc.getErrorStream());
FileOutputStream stderrFile = new FileOutputStream("stderr.txt");
ByteStreams.copy(stderr, stderrFile);
stdout.close();
stderr.close();
stdoutFile.close();
stderrFile.close();
Another option, why not create a run.bat wrapper that will make the redirections?
#echo off
cmd.exe /c run.bat "%1" >> "%2" 2>> "%3"
Use getOutputStream() on the process, instead of using System.out.println(). Sometimes the semantics change between Java implementations.
This seems to be a bugfix actually - the newer implementation makes sense.
Basically, when I type these commands in
the terminal by hand, the sift program works and writes a .key file, but when I try to call it from my program, nothing is written.
Am I using the exec() method correctly? I have looked through the API and I can't seem to spot where I went wrong.
public static void main(String[] args) throws IOException, InterruptedException
{
//Task 1: create .key file for the input file
String[] arr = new String[3];
arr[0] = "\"C:/Users/Wesley/Documents/cv/final project/ObjectRecognition/sift/siftWin32.exe\"";
arr[1] = "<\"C:/Users/Wesley/Documents/cv/final project/ObjectRecognition/sift/cover_actual.pgm\"";
arr[2] = ">\"C:/Users/Wesley/Documents/cv/final project/ObjectRecognition/sift/keys/cover_actual.key\"";
String command = (arr[0]+" "+arr[1]+" "+arr[2]);
Process p=Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader=new BufferedReader(new InputStreamReader(p.getInputStream()));
String line=reader.readLine();
while(line!=null)
{
System.out.println(line);
line=reader.readLine();
}
}
The command line you are using is a DOS command line in the format:
prog < input > output
The program itself is executed with no arguments:
prog
However the command from your code is executed as
prog "<" "input" ">" "output"
Possible fixes:
a) Use Java to handle the input and output files
Process process = Runtime.getRuntime().exec(command);
OutputStream stdin = process.getOutputStream();
InputStream stdout = process.getInputStream();
// Start a background thread that writes input file into "stdin" stream
...
// Read the results from "stdout" stream
...
See: Unable to read InputStream from Java Process (Runtime.getRuntime().exec() or ProcessBuilder)
b) Use cmd.exe to execute the command as is
cmd.exe /c "prog < input > output"
You can't use redirections (< and >) with Runtime.exec as they are interpreted and executed by the shell. It only works with one executable and its arguments.
Further reading:
https://stackoverflow.com/a/11250789/105224
You cannot use input/output redirection with Runtime.exec. On the other hand, the same method returns a Process object, and you can access its input and output streams.
Process process = Runtime.exec("command here");
// these methods are terribly ill-named:
// getOutputStream returns the process's stdin
// and getInputStream returns the process's stdout
OutputStream stdin = process.getOutputStream();
// write your file in stdin
stdin.write(...);
// now read from stdout
InputStream stdout = process.getInputStream();
stdout.read(...);
I test, it's ok. You can try. Good luck
String cmd = "cmd /c siftWin32 <box.pgm>a.key";
Process process = Runtime.getRuntime().exec(cmd);
*For special characters that usually cause problems:
This code works correctly even with file names like: "1 - Volume 1 (Fronte).jpg"
String strArr[] = {"cmd", "/C", file.getCanonicalPath()};
Process p = rtObj.exec(strArr);///strCmd);
Agree too, redirection not supported here.
Tested on Windows 7
{guscoder:912081574}
1) I'm using Java to call Linux terminal to run foo.exe and save the output in a file:
String[] cmd = {"/bin/sh", "-c", "foo >haha.file"};
Runtime.getRuntime().exec(cmd);
2) The problem is when I plan to read haha.file later in the code, it hasn't been written yet:
File f=new File("haha.file"); // return true
in = new BufferedReader(new FileReader("haha.file"));
reader=in.readLine();
System.out.println(reader);//return null
3) Only after the program is done will the haha.file be written. I only know how to flush "Writers" but don't know how to flush sth. like this.
How can I force java to write the file in the terminal?
Thanks in advance
E.E.
This problem is caused by the asynchronous nature of Runtime.exec. foo is being executed in a seperate process. You need to call Process.waitFor() to insure the file has been written.
String[] cmd = {"/bin/sh", "-c", "foo >haha.file"};
Process process = Runtime.getRuntime().exec(cmd);
// ....
if (process.waitFor() == 0) {
File f=new File("haha.file");
in = new BufferedReader(new FileReader("haha.file"));
reader=in.readLine();
System.out.println(reader);
} else {
//process did not terminate normally
}
You can either wait for the completion of the process:
Process p = Runtime.getRuntime().exec(cmd);
int result = p.waitFor();
Or use the p.getInputStream() to read directly from the standard output of the process.