On Windows 7 64 bit, running 64 bit Java 1.7.0_17 , the p.waitFor() shown below never returns.
String move_command="cmd.exe /c xcopy /Y /E "+x86_release+" "+path+"\\";
Process p;
p = Runtime.getRuntime().exec(move_command);
p.waitFor();
If I use Windows Explorer, it looks like all the files are copied (same number, same size, etc.)
If I do the below, it waitFor() does return:
String move_command="cmd.exe /c move /Y "+x86_release+" "+path+"\\";
Process p;
p = Runtime.getRuntime().exec(move_command);
p.waitFor();
What could be so different between an xcopy and a move that keeps waitFor() from returning, or am I on the wrong track entirely?
xcopy probably just happens to produce more output than move, filling up the out-buffer and blocking until it is flushed. The default behavior in Java is to pipe the subprocess's stdout/stderr into InputStreams that you are then required to read programmatically lest the subprocess's buffers overflow.
If the latter is the case, the solution is simple, and in fact you should do that anyway: use ProcessBuilder to prepare the system call and call inheritIO on it. This will reuse your parent process`s stdin and stdout for the subprocess.
A side note, xcopy is a regular .exe file and doesn't need wrapping into cmd.exe /c.
I suspect you're not consuming the process standard out/err, and that's blocking the process. If your code doesn't consume this output, then the spawned process will hang (and you'll hang waiting for that process!). Why the difference in behaviour between the two commands ? Probably due to the quantity of data returned and the impact on the publishing buffers.
See this answer for more details.
I would also investigate Apache Commons FileUtils.copyDirectory() such that you don't have to spawn a whole new process to copy files.
Related
I'm having some trouble getting my app to execute that command, it asks for root access but does not run the kill $(pidof cameraserver) part.
If I run kill $(pidof cameraserver) from the terminal on my phone it works, but not when I hit the button on my app.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button action = findViewById(R.id.buttonAction);
action.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
Process process;
Process secondProcess;
process = Runtime.getRuntime().exec("su kill $pidof(cameraserver)");
BufferedReader in = new BufferedReader
(new InputStreamReader(process.getInputStream()));
}
catch (IOException e){
e.printStackTrace();
}
}
});
}
I doubt that $(pidof cameraserver) will be expanded by the exec() method. This is a shell feature. It works in the terminal, because that's a shell. You would have to exec pidof cameraserver first and use the result of that for a second call to exec.
I also believe, that you can not use the single argument overload of exec this way. You would rather have to provide a string array that contains the command and its parameter as separate array items.
You're confused. Your post title is Executing shell commands, and yet what you're doing is.. not that.
Runtime.exec does not run shell commands.
It runs executables, with its own extremely crappy shell-like behaviour you should not rely on.
Just su, in bash, will apply aliases, check if it's a bash built-in, and if none of those apply, scans the PATH. Runtime.exec does almost none of those things (it kinda scans PATH, but don't rely on that). bash will expand ${pid}, *, and many other things. Runtime.exec doesn't do any of these things.
For starters, take Runtime.getRuntime().exec and put it on your verboten list. You should never ever call this method. Let it join Thread.stop and all those silent char-to-byte conversion methods that don't include a charset.
The right way to do process control is to use ProcessBuilder. This makes a lot more clear what is happening:
Java will run the exact application you provide (so, don't do su, do /bin/su), and pass arguments verbatim with absolutely no parsing whatsoever. With the exec method you use, java will try to apply quote separation and otherwise treats spaces as separators, and does nothing more. This confusing mix of some bashisms (such as splitting your string on spaces and then treating each part as an argument) and lack of bashisms (not applying *.txt, or ${var} and friends, or supporting any of the built-ins) is just going to lead to a lot of confusion.
You have two general options:
Realize java just tells the OS to fire app A with argslist B with no processing whatsoever, and 'bash' is not built into the OS. There is simply no way to do any of these things directly, you have to program them. Whatever pidof does, you'd have to program it in java.
Start bash, not su. Then tell bash to run this command.
option 2 is possibly easier here:
ProcessBuilder pb = new ProcessBuilder();
pb.command("/bin/bash", "-c", "su kill $pidof(cameraserver)");
Process p = pb.start();
Then you're stuck trying to deal with the fact you're calling su, which goes out of its way to stop you from doing this. Having a script just casually pick up root rights is something that is just going to lead to a ton of security issues.
Find another way. For example, make a shell script that runs this command, then use some linux knowhow to give this shell SUID rights (so that it automatically and silently is run with the rights of the owner), then make it owned by root, and then use processbuilder to exec "/bin/bash", "-c", "/path/to/that/script". This has a chance of being safe: That shell can only be written to by root (make sure of that!), and thus you have now decided, as operator of the hardware you run this on, that this specific job is acceptable even for non-root users to run.
NB: If you've configured your su to just let this happen without asking for passwords, go back. Undo that mistake. Keep your system safe. root access is hidden behind a few doors for a reason. Stop replacing the locks on your safe with paper and sticks.
I finally fixed the issue, I have my phone rooted with Magisk so all i had to do was use their 'su' file, I'm new to Java and android development so i don't really know how to explain myself in proper terms but here's the code:
ProcessBuilder pb = new ProcessBuilder();
pb.command("/sbin/su", "-c","pkill cameraserver");
Process p = pb.start();
This limits the user to use Magisk, but since this is for personal use it wont matter much.
Thanks to EricSchaefer and rzwitserloot for their knowledge and help.
I want to execute multiple jar files sequentially by passing I/O and reading the output from them from a single Java program. I used this below code to invoke my jar inside my java code. This is leading to multiple JVM instances and the jar execution is not completing until I stop the execution of my main java program.
Process pb = Runtime.getRuntime().exec("java -jar path/to/jar");
pb.waitFor();
BufferedReader ib = new BufferedReader(new InputStreamReader(pb.getErrorStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(pb.getInputStream()));
System.out.println(in.readLine());
My ideal sequence of execution is:
Main java (starts)
JAR 1 (starts and completed)
JAR 2 (starts and completed)
Jar n ----
Main Java (stops)
My knowledge is limited in multiprocessing that is going on with my current code. Please help me understand how it works and how can I achieve the scenario I intend to.
As soon as the subprocess produces more output than the pipe’s buffering capability, it will be blocked until the initiating process reads the data. When you are waiting for the end of the subprocess before reading anything, this may lead to a deadlock.
Since you are only reading the output to reprint it to the console (or generally, write to stdout), you may use ProcessBuilder to tell it not to use a pipe, but connect the subprocess’ stdio to your process’ stdio:
Process pb = new ProcessBuilder("java", "-jar", "path/to/jar").inheritIO().start();
pb.waitFor();
Then you don’t need to do anything to transfer the subprocess’ output to your process’ output and there’s no deadlock potential.
inheritIO() does the magic. It’s a short-hand for .redirectInput(Redirect.INHERIT) .redirectOutput(Redirect.INHERIT) .redirectError(Redirect.INHERIT). These redirect… calls also can be used for configuring the individual channels to use a pipe or read from/ write to a file.
I am working on java process builder to execute the windows external process(i.e., exe files). I have to get the process info and error using input stream, error stream to write it in some text files.
It's working sometimes and unexpectedly hanging sometimes.
When i invoke around three external process to execute one by one. Two process working and it's getting hang on third process only.
I could see the process exit value as 0 some times it's giving some other random value.
I have read the below blog and applied the same logic to execute the process but's not working for my scenarios.
Could anybody please help me to diagnose these problem...
//Starts to initiate the external process
//This code will pick three arguments from to execute the process sequentially
//it will pass the process commands through for loop one by one
Process p =new ProcessBuilder(s)
.directory(new File(je.getExecution().getWorkingDirectory()))
.redirectErrorStream(true)
.redirectOutput(file)
.start();
p.getOutputStream().close();
int processStatus= p.waitFor();
// if the process exits with 0 normal termination
Thanks in advance..,
The entire thing you are doing is error prone and it’s not worth trying to find out all of the mistakes, as you are making your life unnecessarily hard.
Note that you are calling redirectErrorStream(true), but still are trying to read from the error stream, which makes no sense. The error stream will always be empty under this condition. That said, if you already know about redirections, it’s not clear, why you don’t use them directly, instead of creating threads, manually copying data into StringWriters, etc.
Apparently, you want to use the working directory je.getExecution().getWorkingDirectory(), launch the command s and directing both, output and error, of the program to file. This can be achieved as easy as
new ProcessBuilder(s)
.directory(je.getExecution().getWorkingDirectory())
.redirectErrorStream(true).redirectOutput(file)
.start()
.waitFor();
The only remaining error source is that the launched process could wait for input from your side. Since you apparently don’t want to provide input, you can use
Process proc = new ProcessBuilder(s)
.directory(je.getExecution().getWorkingDirectory())
.redirectErrorStream(true).redirectOutput(file)
.start();
proc.getOutputStream().close();
proc.waitFor();
to denote that you won’t write any data. This will not stop the process, but when it tries to read something from its standard input, it will immediately get an end-of-file without blocking.
Which can be demonstrated with
Process proc = new ProcessBuilder("cmd", "/c", "pause")
.redirectErrorStream(true).redirectOutput(file)
.start();
proc.getOutputStream().close();
proc.waitFor();
I am creating a Process as follows:
Process p = Runtime.getRuntime().exec("nohup /usr/bin/python " + filePath + " &");
Process q = Runtime.getRuntime().exec("/usr/bin/python -m -webbrowser " + url);
I am trying to run the p in the background so that q can run without a problem. Also, when the Java program ends, the python script is no longer running.
The command you ran is nohup, and it did exit immediately. nohup started a separate Python process, but that's not the process p is controlling.
Why are you using nohup and & if that isn't the functionality you want? If you simply want to execute a Python subprocess just call:
Process p = Runtime.getRuntime().exec("/usr/bin/python " + filePath);
Note that it's also safer to use the overload of exec that takes an array, so you don't have to do string-concatenation yourself (and therefore avoid a type of security exploit). The syntax changes slightly to:
Process p = Runtime.getRuntime().exec(new String[]{"/usr/bin/python", filePath});
Or with ProcessBuilder:
Process p = new ProcessBuilder("/usr/bin/python", filePath).start();
Take a look at the documentation for the & operator here. Your process is running in the background. As far as the Java program you're writing (and the shell, for that matter) are concerned, the call you made has already ended. It's most certainly still running in the background, but you're telling the shell (and by extension your Java program) to not pay attention to the output and move on.
As Sean Bright mentioned take the '&' character out of the end of the script that you are running. Basically on all *nix systems. If you call make a type a command and use the ampersand, it will run that process in the background. That's why the call to java process is returning finished.
It would probably be better to not execute the command in the background (no '&') and just call Process.isAlive() periodically to determine if the process is finished.
I am using java to call arp -s command and waiting for the process to finish in order to complete function .
String command ="arp -s "+entryIpAddress+" ee-ee-ee-ee-ee-ee-ee";
Process p=Runtime.getRuntime().exec(command);
p.waitFor();
but the calling of this process is taking more than the usual time so, is there a way to increase the performance of this method. i cannot remove the p.waitFor() my next function depend on the added entry.
Your code doesn't show any sign of effort towards allowing the process to complete ever. You are not consuming its output, so its output buffer will soon fill up, blocking further progress.
To make this task easier on yourself, please use the ProcessBuilder.