I'm doing a simple java compiler. My program is running, but if it is going to scan, it will not receive input and will freeze.
A small code from my compiler
public class ProcessBuilderMultipleCommandsExample {
static String backSlashFl = "C:\\Users\\xxA\\Desktop";
public static void main(String[] args) throws InterruptedException,
IOException {
// multiple commands
// /C Carries out the command specified by string and then terminates
ProcessBuilder pbC = new ProcessBuilder( //COMPİLE
"cmd.exe", "/c", "cd " + backSlashFl + "&& javac " + "Test" + ".java");
Process processC = pbC.start();
ProcessBuilder pb = new ProcessBuilder( //RUN
"cmd.exe", "/c", "cd " + backSlashFl + "&& java " + "Test");
Process process = pb.start();
IOThreadHandler outputHandler = new IOThreadHandler(
process.getInputStream());
outputHandler.start();
process.waitFor();
System.out.println(outputHandler.getOutput());
}
private static class IOThreadHandler extends Thread {
private InputStream inputStream;
private StringBuilder output = new StringBuilder();
IOThreadHandler(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
Scanner br = null;
try {
br = new Scanner(new InputStreamReader(inputStream));
String line = null;
while (br.hasNextLine()) {
line = br.nextLine();
output.append(line
+ System.getProperty("line.separator"));
}
} finally {
br.close();
}
}
public StringBuilder getOutput() {
return output;
}
}
}
I think it's working in the back, but how do I get the input part?
Here's the file I want to compile and run.
import java.awt.*;
import java.awt.event.*;
import java.util.Scanner;
public class numberScan {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("Enter the number: ");
int a=scan.nextInt();
System.out.println("Number= " + a);
}
}
I'm waiting for your help.
Editted
Now when I run the GUI, the Run key is pressed. What do you think I should do?
buttonRun.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
String backSlashFl = file.getAbsolutePath().replace("\\", "\\\\");
backSlashFl = backSlashFl.replace(flName + ".java", "");
try {
execute("cmd.exe", "/c", "cd " + backSlashFl + " && java " + flName);
JOptionPane.showMessageDialog(null, "Dosya çalıştı!","Bilgilendirme",
JOptionPane.INFORMATION_MESSAGE);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException ae) {
// TODO Auto-generated catch block
ae.printStackTrace();
} catch (Exception e2){
e2.printStackTrace();
}
}
});
When the GUI app runs, this run button remains.
There are several issues with the given code. I tried to squeeze it into a comment, but now decided to extend it to an answer:
The class that you want to compile is called numberScan, but obviously stored in a file called Test.java. A public class can only be stored in a file that has the same name as the class. Call the class NumberScan, and call the file NumberScan.java.
You are only trying to print the output that is provided by the input stream. But you are not printing the result that is provided by the error stream (so if there are errors, you will not see them!). Also, you only printed the output of the Process process (which is used for running the program). You did not print the output of the Process processC, which was used for compiling the program.
The reason of why you don't see any output is that the line
System.out.println(outputHandler.getOutput());
is executed before the process is finished. You used waitFor, but the output is filled by a different thread - namely, the IOThreadHandler. The getOutput method could only be called after the IOThreadHandler has finished, but if you want to continuously update the output, then this will not work.
It is not entirely clear what you want to achieve, but guessing from the code that you provided, your goal seems to be to create a program that
Compiles the given Java file
Executes the resulting Java program
Prints possible error messages and the output that is created by the program
Important: Allows interacting with the program, in the sense that it should be possible to send input to the System.in of the program.
The last two points are particularly hard to achive manually. You would have to set up threads for reading the input stream and the error stream. These would require some trickery to make sure that the data is read continuously while the program is executed. Additionally, you would have to set up a thread that forwards the data that the user enters to the Java program that is executed in its own process.
Fortunately, all this has become fairly trivial with Java 7: You can simply set an appropriate ProcessBuilder.Redirect for all the streams (namely, the redirect INHERIT), so that all the streams are mapped to the corresponding streams of the surrounding program.
Here is an example:
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.Arrays;
public class ProcessBuilderMultipleCommandsExample {
private static String path = "C:\\Users\\xxA\\Desktop";
public static void main(String[] args)
throws InterruptedException, IOException {
execute("cmd.exe", "/c", "cd " + path + " && javac " + "NumberScan" + ".java");
execute("cmd.exe", "/c", "cd " + path + " && java " + "NumberScan");
}
private static void execute(String ... commands)
throws IOException, InterruptedException
{
System.out.println("Executing "+Arrays.asList(commands));
ProcessBuilder processBuilder = new ProcessBuilder(commands);
processBuilder.redirectInput(Redirect.INHERIT);
processBuilder.redirectOutput(Redirect.INHERIT);
processBuilder.redirectError(Redirect.INHERIT);
Process process = processBuilder.start();
process.waitFor();
}
}
Related
How to print commands to bat/cmd file using Java? I have created a method that opens this bat file and now the program should write commands to this bat file. For instance, I have a string variable "Command" and the program must write this command to bat file.
Here I attach the code.
private static void openBat(){
File file = new File(lockerPath);
try {
if (file.exists()) {
Process pro = Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + lockerPath);
pro.waitFor();
} else {
System.out.println("file does not exist");
}
} catch (Exception e) {
System.out.println(e);
}
}
This is the code to open bat file, and the next code is to write commands:
private static void printing(int password ){
try {
ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/C", lockerPath);
Process process = processBuilder.start();
process.waitFor();
List<String> commands = new ArrayList<>();
commands.add(String.valueOf(password));
processBuilder.command(commands);
}catch (Exception ex){
ex.printStackTrace();
}
}
It doesn't write anything to the file.
I will be very grateful for your help.
I still don't think I completely understand your question. If you just want to simulate the user entering a value to a batch file via a java program, then the below code does that.
First I wrote a batch file.
#echo off
set /P pw=
echo You entered: %pw%
It simply waits for the user to enter a value and assigns that value to a variable named pw. After the user enters the value, the batch file displays the entered value.
Here is the java code that runs the above batch file and enters a value.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", "getusrpw.bat");
try {
Process p = pb.start(); // throws java.io.IOException
BufferedReader stdout = p.inputReader();
BufferedReader stderr = p.errorReader();
BufferedWriter stdin = p.outputWriter();
stdin.write("secret");
stdin.newLine();
stdin.flush();
String output = stdout.readLine();
while (output != null) {
System.out.println("OUT> " + output);
output = stdout.readLine();
}
String error = stderr.readLine();
while (error != null) {
System.out.println("ERR> " + error);
error = stderr.readLine();
}
int exitStatus = p.waitFor(); // throws java.lang.InterruptedException
System.out.println("Process exit status = " + exitStatus);
}
catch (InterruptedException | IOException x) {
x.printStackTrace();
}
}
}
The name of the batch file is getusrpw.bat.
stdout is for reading output generated by the batch file.
stderr is for reading error output. Note that there may be no error output.
stdin is for sending input to the batch file.
Note that methods inputReader, outputWriter and errorReader were added in JDK 17. If you are using an earlier version, use methods getInputStream, getOutputStream and getErrorStream, respectively.
The Java program enters the value secret.
The Java program prints the output generated by the batch file.
When I run the above Java code, it produces the following output:
OUT> You entered: secret
Process exit status = 0
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Goal: to initialise a JVM(2) from a separate JVM(1) using ProcessBuilder, capturing the resulting output from JVM(2) and displaying the result within a JTextArea in JVM(1).
Situation: able to launch JVM(2) from within JVM(1) and capture the resulting output from JVM(2) to a JTextArea within the JVM(1).
Problem: the JVM(2) will not respond to input until JVM(1) is terminated.
Thread inside VJM(1) that starts JVM(2):
Runnable runnable = () -> {
try {
JVMBooter.startSecondJVM();
} catch (Exception ex) {
Logger.getLogger(MyMenu.class.getName()).log(Level.SEVERE, null, ex);
}
};
Thread t = new Thread(runnable);
t.start();
JVMBooter source code:
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
public class JVMBooter {
public static void startSecondJVM() throws Exception {
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "startscript.bat");
File dir = new File("D:/Server");
pb.directory(dir);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
( (line = reader.readLine()) != null && ! line.trim().equals("--EOF--")) {
OutputFrame.textArea.append(line + "\n");
}
}
}
The JVM(2) is started within the startscript.bat file with:
java -jar server.jar
Depending on the situation it may be necessary to read the error stream instead of the input stream, e.G. if your second java call is -version or the program you call only writes to stderr instead of stdout getting the Error Stream is the correct approach.
I wrote this MCVE:
import java.io.*;
public class Starter {
public static void main(String[] args) throws IOException {
ProcessBuilder pb = new ProcessBuilder("./dosomething.sh");
File dir = new File(new File(File.listRoots()[0], "tmp"), "2jvm");
pb.directory(dir);
Process p = pb.start();
BufferedReader read = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line;
while ( (line = read.readLine() ) != null) {
System.out.println("line: " + line);
}
}
}
and in 'dosomething.sh' this:
echo before
java -version
echo after
when I use p.getInputStream I would get the 'before' and 'after'. When I use p.getErrorStream I get the Java Version Information.
That might be true for you too. I suggest you add echo lines in your batch file to see if they get printed out.
I also wrote a simple hello world and when I called that from dosomething.sh it got printed to the input stream as expected. It is a weird quirk of -version to write to stderr.
For completeness here is the Hello World I used (it has waits to simulate a longrunning server process):
public class Caller {
public static void main(String[] args) {
synchronized(Caller.class) {
for(int ii = 0; ii < 10; ii++) {
try {
Caller.class.wait(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("Hello world! " + ii);
}
}
}
}
I'm building a simple UI for ffmpeg launching ffmpeg.exe with parameters using exec(). it works on Os X but on Windows 7-8 after few seconds the ffmpeg process suspends itself and resumes only when I kill the father process. (also ddlhost.exe is created)
Ffmpeg.exe converts successfully the same video from cmd.
Searching on the internet I've found this answer but I have the same problem running a simple test program which is not using the Input and Error streams.
Here is the test program code which has the same problem of the main one:
public class Main {
static String param_ffmpeg_1 = "./data/ffmpeg.exe";
static String param_ffmpeg_2 = "-i";
static String in = "./data/source.mov";
static String out = "./data/out.flv";
static Process p;
public static void main(String[] args) {
/*File f = new File(out);
if (f.exists()){
f.delete();
}*/
Runtime rt = Runtime.getRuntime() ;
//String cmd1 = param_ffmpeg_1 + param_ffmpeg_2 + in_path + param_ffmpeg_3 + out_path ;
System.out.println(in);
System.out.println(out);
String[] cmd1 = new String[] { param_ffmpeg_1, param_ffmpeg_2, in, "-ar", "44100", "-vb", "2500k", "-s", "882x496", "-f", "flv", out};
try {
p = rt.exec(cmd1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int r = 123456;
try {
r = p.waitFor();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(r);
}
}
Does ffmpg write anything to the stdout or stderr? If yes you have to consume that. (In seperate threads as you need to consume the stderr and the stdout in parallel) see Process.getInputStream() and Process.getErrorStream() for the details. When the buffer is buffer is full your called programm is stopped and hangs.
The fact that it works in OS/X but not Windows might be caused by different buffer sizes.
You should call getInputStream() and getErrorStream() on the Process returned by Runtime.exec and drain that all the time the process is running. If you do not then it will eventually fill up and block and stop the process from running.
See Java Process with Input/Output Stream for examples.
From the accepted answer
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();
InputStream itsOutput = process.getInputStream();
// Wrap the stream in a Reader ...
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
I am trying to execute .sql file from java. It runs successfully when I put exit at the end of the .sql file. Is there any possibility to run without giving exit in .sql?
Java code
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStreamReader;
public class Test {
private static String script_location = "";
private static String file_extension = ".sql";
private static ProcessBuilder processBuilder =null;
public static void main(String[] args) {
try {
//D:/Tset is the folder that contains the.sql files
File file = new File("D:/Tset");
File [] list_files= file.listFiles(new FileFilter() {
public boolean accept(File f) {
if (f.getName().toLowerCase().endsWith(file_extension))
return true;
return false;
}
});
for (int i = 0; i<list_files.length;i++){
script_location = "#" + list_files[i].getAbsolutePath();
//ORACLE
processBuilder = new ProcessBuilder
("sqlplus","user56/password", script_location); //ORACLE
//script_location = "-i" + list_files[i].getAbsolutePath();
// processBuilder =
new ProcessBuilder("sqlplus",
"-Udeep-Pdumbhead-Spc-de-deep\\sqlexpress-de_com",script_location);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
BufferedReader in =
new BufferedReader(new InputStreamReader(process.getInputStream()));
String currentLine = null;
while ((currentLine = in.readLine()) != null) {
System.out.println(" " + currentLine);
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
Oracle script file:
createtable.sql
createtable.sql
create table t1(empname varchar2(20),address varchar2(20))
/
create table t2(name varchar2(20),lname varchar2(20))
/
exit;
insertvalue.sql
insert into t1 values('aaaaa','chennai')
/
insert into t2 values('bbbbb','ddddd')
/
exit;
If I don't put an exit at last it simply runs the first file only. Is there a solution to this?
In your code you run the command sqlplus in a loop so you must end every command (using exit to exit sqlplus).
If you want to call many scripts on the same sqlplus instance then maybe you can create "on the fly" a script that has commands such as:
onTheFlyAcript.sql
#createtable.sql <your params>
#insertvalue.sql <your params>
exit;
So basically you can do something like:
try {
Printwriter out = new PrintWriter(new FileWriter("D:/Test/onTheFlyScript.sql"));
for (int i = 0; i<list_files.length;i++){
script_location = "#" + list_files[i].getAbsolutePath();
out.println("START script_location");
}
out.close();
} catch (IOException e){
e.printStackTrace();
}
// note that this command is not in the loop, it's just for running the new script you've created
processBuilder = new ProcessBuilder
("sqlplus","user56/password", "D:/Test/onTheFlyAcript.sql");
This will probably have to be debuged, but the idea is that instead of executing all your scripts one by one with sqlplus, you create a new script that contains the calls to all your scripts.
Now you can remove the exit sqlplus command from all your scripts since you don't need to exit sqlplus (only in onTheFlyScript.sql)
I know about using Runtime.exec, you pass it a native program to run + arguments. If it's a regular program, you can run it directly. If it's a shell script, you have to run an external shell program like sh or csh or cmd.exe.
Is there some Java class (either standard or open-source) that implements a shell, meaning a program that you pass a command string or a script into, that executes commands and redirects standard I/O/err accordingly, so that you could pass a string like foo | bar > baz.out in, and it would run the foo and bar programs w/o having to run another executable outside of Java?
(and by shell I don't mean BeanShell or the standalone Rhino Javascript interpreter, those are Java implementations to execute Java and Javascript code. I'm talking about Java implementations to execute non-Java executables and handle the plumbing of redirecting I/O.)
Ok, I've worked it out:
Basically, you need to invoke bash with a "-s" and then write the full command string to it.
public class ShellExecutor {
private String stdinFlag;
private String shell;
public ShellExecutor(String shell, String stdinFlag)
{
this.shell = shell;
this.stdinFlag = stdinFlag;
}
public String execute(String cmdLine) throws IOException
{
StringBuilder sb = new StringBuilder();
Runtime run = Runtime.getRuntime();
System.out.println(shell);
Process pr = run.exec(cmdLine);
BufferedWriter bufWr = new BufferedWriter(
new OutputStreamWriter(pr.getOutputStream()));
bufWr.write(cmdLine);
try
{
pr.waitFor();
} catch (InterruptedException e) {}
BufferedReader buf = new BufferedReader(
new InputStreamReader(pr.getInputStream()));
String line = "";
while ((line = buf.readLine()) != null)
{
sb.append(line + "\n");
}
return sb.toString();
}
}
Then use it like this:
ShellExecutor excutor = new ShellExecutor("/bin/bash", "-s");
try {
System.out.println(excutor.execute("ls / | sort -r"));
} catch (IOException e) {
e.printStackTrace();
}
Obviously, you aught to do something with the error string but this is a working example.
Since JDK 1.5 there is java.lang.ProcessBuilder which handles std and err streams as well. It's sort of the replacement for java.lang.Runtime
You've always been able to handle streams with Runtime.exec
e.g.
String cmd = "ls -al";
Runtime run = Runtime.getRuntime();
Process pr = run.exec(cmd);
pr.waitFor();
BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line = "";
while ((line=buf.readLine())!=null) {
System.out.println(line);
}
However, if you want to put shell characters such as pipe and redirect in there you'd have to write your own command line parser which links up the streams. As far as I know there hasn't one been written. That being said, could you just invoke bash from Java with a -c "ls | sort" for example and then read the input. Hmm time to do some testing.
You can use the ProcessBuilder API provided by java.
Runtime.getRuntime().exec(...) take either an array of strings or a single string. The single-string overloads of exec() will tokenise the string into an array of arguments, before passing the string array onto one of the exec() overloads that takes a string array. The ProcessBuilder constructors, on the other hand, only take a varargs array of strings or a List of strings, where each string in the array or list is assumed to be an individual argument. Either way, the arguments obtained are then joined up into a string that is passed to the OS to execute.
Find more details at the below link
Difference between ProcessBuilder and Runtime.exec()
Sample program to execute the commands.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.List;
public class ProcessBuilderTest {
static ProcessBuilder processBuilder = null;
static Process spawnProcess = null;
static int exitValue;
static int pid;
static List<String> commands;
public static void main(String[] args) {
runSqoop();
}
public static void runSqoop() {
String[] commands = { "ssh", "node", "commands" };
processBuilder = new ProcessBuilder(commands);
try {
System.out.println("Executing " + commands.toString());
spawnProcess = processBuilder.inheritIO().start();
try {
exitValue = spawnProcess.waitFor();
pid = getPID(spawnProcess);
System.out.println("The PID is " + pid);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("Process exited with the status :" + exitValue);
} catch (Exception e) {
e.printStackTrace();
}
}
public static int getPID(Process process) {
try {
Class<?> processImplClass = process.getClass();
Field fpid = processImplClass.getDeclaredField("pid");
if (!fpid.isAccessible()) {
fpid.setAccessible(true);
}
return fpid.getInt(process);
} catch (Exception e) {
System.out.println(e.getMessage());
return -1;
}
}
}