Try to figure out what I am missing (I'm still quite green on some advanced programming concepts, this is probably why I don't fully grasp my issue): I am creating an app which allows user, among other things, to open specific documents, apps or command line instructions the user types in.
Where I fail to have it working is to try to create a ProcessBuilder object to test a user-typed-in command line (macOS and Windows complaint). For a general purpose command line, e.g. ls (macOS) or dir (Windows), my code is doing fine. But when I try a command line which would require user interaction, I fail to have the output/input/error streams to properly work. I saw many related posts about issues, but none worked for me, so I hope I didn't miss out on one (sorry if I did).
Post "Java ProcessBuilder process waiting for input" seemed the closest for me but I get grief on line new Thread(new StreamGobbler("in", out, inStream)).start(); from my IDE stating that a non-static variable this cannot be referenced from a static method.
Here is the code that I got to finally run. It simply stops at value = is.read(); when prompt reaches the last character to display to user (EOF maybe? In may case, it asks user whether the target is a file or a folder), which I understand is because the input stream listening is not actually finished as such.
Please note that the "xcopy" command is ONLY to illustrate my issue, the actually command line can be virtually any command line the user would enter in a field within my application.
class Test {
public static void main(String[] args) {
try {
String command = "xcopy <file1.txt> <file2.txt> /L /Y";
//Following line is specific to Windows, where "cmd /c" should be
//pre-appended to have command line to properly work.
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", command);
pb.redirectErrorStream(true);
Process p = pb.start();
InputStreamPass isc = new InputStreamPass(p.getInputStream());
InputStreamPass errsc = new InputStreamPass(p.getErrorStream());
OutputStreamExport ose = new OutputStreamExport(p.getOutputStream());
ose.run();
errsc.run();
isc.run();
int exitCode = p.waitFor();
System.out.println("Exit code: " + exitCode);
} catch (SecurityException | IOException | InterruptedException | NullPointerException ex) {
System.err.println(ex);
}
}
private static class InputStreamPass implements Runnable {
private final InputStream is;
InputStreamPass(InputStream is) {
this.is = is;
}
@Override
public void run() {
try {
String message = "";
int value = is.read();
while (value != -1) {
System.out.print((char)value);
message += (char)value;
//Code blocks at following line when reaches
//end of prompt, waiting for user input, so value never
//gets to be assigned the final is.read() value
value = is.read();
}
System.out.println(message);
} catch (IOException ex) {
System.err.println(ex);
}
}
}//private static class InputStreamConsumer extends Thread
private static class OutputStreamExport implements Runnable {
private final OutputStream ots;
OutputStreamExport(OutputStream ots) {
this.ots = ots;
}
@Override
public void run() {
try {
ots.flush();
} catch (IOException ex) {
System.err.println(ex);
}
}
}//private static class InputStreamConsumer extends Thread
And here is the output (localized form):
Est-ce que file2.txt désigne un nom de fichier ou un nom de répertoire de la destination (F = fichier, R = répertoire)ÿ?
-or rough back-translation:
Does file2.txt relates to the filename or the foldername of the target (F = file, D = directory)?
Any help is more than welcome (I'm already 3 days searching and testing to no avail) :)
copyinstead ofxcopyxcopyis waiting for input on its stdin, which is (the other end of) the pipeProcess.getOutputStream. Nothing in your code ever writes anything to that stream, soxcopywaits forever. If you do want to write something, like in this case a line containingD, then put code in your program that does that.inScanner.nextLine()if this is the last output line and command line is waiting for user input.