I am trying to compile a java program using the ProcessBuilder but everytime i see this error being present on the console even though the file is present at that path.
ERROR
java.io.IOException: Cannot run program "javac
/Users/foo/Desktop/online-compiler/user1455523443383/Main.java":
error=2, No such file or directory
#Override
public ProgramResult executeProgram(File program) throws IOException {
String parent = program.getParentFile().getParentFile().getAbsolutePath();
String[] commands = new String[]{
"javac "+program.getAbsolutePath(),
// "cd "+parent,
// "java -cp "+parent+" "+PACKAGE_NAME+"."+MAIN_CLASS
};
ProcessBuilder builder = new ProcessBuilder(commands);
builder.redirectErrorStream(true);
Process executorProcess = builder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(executorProcess.getInputStream()));
String line;
StringBuilder sb = new StringBuilder();
while((line = reader.readLine())!=null) {
sb.append(line);
}
reader.close();
ProgramResult result = new ProgramResult();
result.setOutput(sb.toString());
return result;
}
Some more information
Javac is on the path, as running it(without the file) via ProcessBuilder is printing the help options.
OS : MACOSX
Conclusions from this questions are
1) ProcessBuilder needs every argument to the command as a separate index , like to execute "javac filename.java" you write this
new String[] {"javac" , "filename.java"}
2) To execute multiple commands you should be using the following trick
new String[]{
"/bin/bash",
"-c",
"javac "+
program.getAbsolutePath()+
" &&" +
" java -cp " +
parent +
" "+ PACKAGE_NAME+"."+MAIN_CLASS,
}
A big thanks to #kucing_terbang for really digging in this problem with me to solve it.
AFAIK, if you want to put an argument into the ProcessBuilder, you should put in into another index of the array.
So, try change the command variable into something like this and try again.
String[] commands = new String[]{"javac", program.getAbsolutePath()};
If you want to compile a Java class, better use JavaCompiler acquired from ToolProvider.getSystemJavaCompiler();
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
javaCompiler.run(null, null, null, program.getAbsolutePath());
Related
I am trying to pass a string to a Linux command using <<<:
cat <<< 'Hello'
While this works perfectly in the terminal, Java does not execute this
String cmd = "cat <<< 'Hello'";
Process p = new ProcessBuilder(cmd.split(" ")).start();
String stderr = IOUtils.toString(p.getErrorStream(), Charset.defaultCharset());
String stdout = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());
System.out.println(stderr);
with an error from terminal:
cat: '<<<': No such file or directory
cat: "'hello'": No such file or directory
Why is that so? Commands without <<< get executed in the usual manner.
As others have pointed out, <<< is a capability of shells, like bash. You could invoke bash from your ProcessBuilder… but really, you don’t need a here-string. You have Java.
Here’s how to pass known input to a command:
String input = "Hello";
ProcessBuilder builder = new ProcessBuilder("cat");
builder.inheritIO();
builder.redirectOutput(ProcessBuilder.Redirect.PIPE);
Process p = builder.start();
try (Writer processInput =
new OutputStreamWriter(p.getOutputStream(), Charset.defaultCharset())) {
processInput.write(input);
}
The inheritIO method of ProcessBuilder will cause the subprocess’s standard output to appear in the Java program’s standard output, and similarly, the subprocess’s standard error will appear in the Java program’s standard error.
If you want to capture the standard output of the process, you can replace inheritIO() with an explicit call to redirectError:
String input = "Hello";
ProcessBuilder builder = new ProcessBuilder("cat");
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
Process p = builder.start();
try (Writer processInput =
new OutputStreamWriter(p.getOutputStream(), Charset.defaultCharset())) {
processInput.write(input);
}
try (BufferedReader processOutput = new BufferedReader(
new InputStreamReader(p.getInputStream(), Charset.defaultCharset()))) {
String line;
while ((line = processOutput.readLine()) != null) {
// ...
}
}
Try this way:
processBuilder.command("bash", "-c", cmd);
In general output redirection is shell feature and ProcessBuilder is not able to understand the command in the way you gave it.
I have simple java program for compiling java classes.
I created a JAR of this program and when I run it on Ubuntu I pass to the jar the path of folder with java files.
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
compile(args[0]);
}
//pathToFiles - is a value from command line arguments
private static void compile(String pathToFiles) throws IOException, InterruptedException {
List<String> cmdList = new ArrayList<>();
cmdList.add("javac");
cmdList.add(pathToFiles);
System.out.println("cmd: "+cmdList);
ProcessBuilder pb = new ProcessBuilder(cmdList);
Process process = pb.start();
int exitValue = process.waitFor();
if (exitValue != 0) {
generateCompileException(process);
}
}
//method just generates error message if there was an error
private static void generateCompileException(Process process){
StringBuilder response = new StringBuilder();
try (final BufferedReader b = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
if ((line = b.readLine()) != null)
response.append(line);
} catch (final IOException e) {
e.printStackTrace();
}
throw new RuntimeException(response.toString());
}
}
When I pass path containing single java file it works:
java -jar co-1.jar /home/admin/test2/Calculator.java
But I want to compile multiple files. When I pass path containing multiple files I get error: file not found.
java -jar co-1.jar '/home/admin/test2/*.java'
PS: If I run a javac command manually with multiple files, it will work:
###################################
UPDATE:
I've added bash command to ProcessBuilder:
private static void compile(String pathToFiles) throws IOException, InterruptedException {
List<String> cmdList = new ArrayList<>();
cmdList.add("bash");
cmdList.add("-c");
cmdList.add("javac");
cmdList.add(pathToFiles);
System.out.println("Processor builder command: "+cmdList);
ProcessBuilder pb = new ProcessBuilder(cmdList);
Process process = pb.start();
int exitValue = process.waitFor();
if (exitValue != 0) {
System.out.println("Finished with error. Exit value: "+exitValue);
generateCompileException(process);
}
}
But process withished with error code 2 with empty response from ProcessBuilder.
PS: RuntimeException was thrown by this line: throw new RuntimeException(response.toString());
ProcessBuilder will not evaluate wildcards, as that is a feature of your terminal (such as bash). If you want wildcard to be expanded you need to run bash inside ProcessBuilder command, such as:
String commandContainingWildcard = "javac /blah/*.java";
ProcessBuilder pb = new ProcessBuilder("bash", "-c", commandContainingWildcard);
... // start() etc
For the above to work you need to have "bash" or whatever shell you use in your path, otherwise you will need to use full path to bash (such as "/bin/bash").
The third argument for command to compile must exactly match what works inside your terminal and must be the entire value not "javac" followed by wildcard. Remove single quotes around *.java (so that ProcessBuilder is provided with three command line parameters, not four or more).
However I suggest that ProcessBuilder with bash isn't the best way to do this work. You could try Java compiler tool interface, and get rid of wildcard by easy use of Files.find(dir, 1, (p,a) -> p.getFileName().toString().endsWith(".java")) to scan for all java files and join the paths explicitly for compilation.
UPDATE
Having now resolved your problem you may now find that the javac process fails / freezes due the incorrect way you read the stderr stream - this needs to happen at same time as stdout and before process.waitFor(). An easy fix is to consume stdout+stderr together:
ProcessBuilder pb = new ProcessBuilder(cmdList);
pb.redirectErrorStream(true);
Process process = pb.start();
ByteArrayOutputStream response = new ByteArrayOutputStream();
process.getInputStream().transferTo(response);
int exitValue = process.waitFor();
if (exitValue != 0) {
System.out.println("Finished with error. Exit value: "+exitValue);
throw new RuntimeException(new String(response.toByteArray()));
}
Remove quotes and use the command as below.
java -cp co-1.jar:/home/admin/test2/* Main.class <args>
See also
https://docs.oracle.com/javase/7/docs/technotes/tools/windows/classpath.html
PS: Unix uses :(colon) as delimiter and windows uses ;(semi-colon) delimiter to separate multiple paths.
I tried this but it didin't work.
My method to launch command prompt commands:
public static void executeCommand(String command, String path){
File folder = new File(path);
ProcessBuilder pb = new ProcessBuilder(command);
pb.directory(folder.getAbsoluteFile());
pb.redirectErrorStream(true);
try{
pb.start();
}catch (IOException e){
e.printStackTrace();
}
}
`
and the code to call the methods:
executeCommand("javac "+str+".java", path);
executeCommand("java "+str, path);
But it's throwing an IOException. I was wondering if this was the wrong way to do it and how I can fix it. "str" is the .java file. The path is the path of the folder to run it in.
There's actually a standard way to do this in Java - using the JavaCompiler class. You require that tools.jar is available on your classpath, though.
The documentation on that page should just about get you started, but for completeness, here is an example.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, Locale.US, Charset.defaultCharset());
File[] files = new File[]{new File("/path/to/java/source/file/you/want/to/compile")};
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
Iterable<? extends JavaFileObject> compilationUnits1 =
fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files));
Boolean call = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits1).call();
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
System.out.println(diagnostic);
}
System.out.println("compiled successfully: " + call);
If you're going to compile the contents of an entire directory, then you would want to change your array slightly:
File directory = new File("/path/to/directory");
File[] files = directory.listFiles();
You need to say where javac and Java are, the directory you pass in is the directory of the file not the directory of the binary of the process you want to run. Either qualify javac / java fully or set the directory to be where the executable is
You are getting IOException
java.io.IOException: Cannot run program "javac ":
You need to separate the arguments into separate strings like this :
executeCommand(new String[]{"javac",str+".java"},path);
/\
||
Please notice No Space like this "javac "
^^
Remove(Space)
Also Change the Argument List in your executeCommand method like this :
public static void executeCommand(String [] command, String path)
I have just tried and Program executed successfully after removing space from "javac" .
Alternatice way to Execute command
You Can use Runtime#exec() method
public static void executeCommand(String command){
try{
Process process = Runtime.getRuntime().exec(command);
InputStream error = process.getErrorStream();
InputStreamReader isrerror = new InputStreamReader(error);
BufferedReader br = new BufferedReader(isrerror);
String line=null;
while ((line = bre.readLine()) != null) {
System.out.println(line);
}
process.waitFor();
}catch (Exception e){
e.printStackTrace();
}
}
Call them
executeCommand("javac "+str+".java");
executeCommand("java "+str);
I have used the ErrorStream to check wheather there is a compile time Error in .java file i am compiling
How am I to execute a command in Java with parameters?
I've tried
Process p = Runtime.getRuntime().exec(new String[]{"php","/var/www/script.php -m 2"});
which doesn't work.
String[] options = new String[]{"option1", "option2"};
Runtime.getRuntime().exec("command", options);
This doesn't work as well, because the m parameter is not specified.
See if this works (sorry can't test it right now)
Runtime.getRuntime().exec(new String[]{"php","/var/www/script.php", "-m", "2"});
Use ProcessBuilder instead of Runtime#exec().
ProcessBuilder pb = new ProcessBuilder("php", "/var/www/script.php", "-m 2");
Process p = pb.start();
The following should work fine.
Process p = Runtime.getRuntime().exec("php /var/www/script.php -m 2");
Below is java code for executing python script with java.
ProcessBuilder:
First argument is path to virtual environment
Second argument is path to python file
Third argument is any argumrnt you want to pass to python script
public class JavaCode {
public static void main(String[] args) throws IOException {
String lines = null;
ProcessBuilder builder = new ProcessBuilder("/home/env-scrapping/bin/python",
"/home/Scrapping/script.py", "arg1");
Process process = builder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((lines = reader.readLine())!=null) {
System.out.println("Line: " + lines);
}
}
}
First is virtual environment path
I'm trying to use cmd.exe to search for a file in a specific directory and then display the path in a java program and write it to a file. The problem is that the process never terminates.
Here is my code:
String[] str = new String[] { "cmd.exe ", "cd c:\\",
" dir /b /s documents", "2>&1" };
Runtime rt = Runtime.getRuntime();
try{
Process p = rt.exec(str);
InputStream is =p.getInputStream();
InputStreamReader in = new InputStreamReader(is);
StringBuffer sb = new StringBuffer();
BufferedReader buff = new BufferedReader(in);
String line = buff.readLine();
while( line != null )
{
sb.append(line + "\n");
line = buff.readLine();
}
System.out.println( sb );
File f = new File("test.txt");
FileOutputStream fos = new FileOutputStream(f);
fos.write(sb.toString().getBytes());
fos.close();
}catch( Exception ex )
{
ex.printStackTrace();
}
Please try
cmd /c
instead of simply
cmd
Reference
Runtime.exec doesn't work that way. You can't pass multiple commands like that to cmd.exe.
Runtime.exec allows you to execute a single process with a list of arguments. It does not provide any "shell" operations (like 2>&1 for instance). You must do that sort of IO redirection yourself using the Input/Output streams.
It's similar to calling another program's main function.
You could try `Runtime.exec( new String[] { "cmd.exe", "/c", "dir", "C:\\" } );
But realistically, if you want file listings, you're much better off using the facilities in the java.io.File class, which won't depend on operating system specific features.
why are not using Java to do directory traversal instead of calling external shell command? It makes your code not portable!
You must use the start command in addition to the cmd.exe process with the /C or /K switch BEFORE the start command. Example: to convert the Windows's command interpreter in a bash console (from the mingw prroject) you must invoke the exec method of the Runtime class with the command "C:\Windows\System32\cmd.exe /C start C:\mingw\msys\1.0\bin\bash.exe" (I use an external command rather than an internal because it's more signifiant but you can use internal command like DIR and so on).