Runtime.Exec Not Working

Why does Runtime.exec(String) work for some but not all commands?


Why do some commands fail?

This happens because the command passed to Runtime.exec(String) is not executed in a shell. The shell performs a lot of common support services for programs, and when the shell is not around to do them, the command will fail.

When do commands fail?

A command will fail whenever it depends on a shell features. The shell does a lot of common, useful things we don't normally think about:

  1. The shell splits correctly on quotes and spaces

    This makes sure the filename in "My File.txt" remains a single argument.

    Runtime.exec(String) naively splits on spaces and would pass this as two separate filenames. This obviously fails.

  2. The shell expands globs/wildcards

    When you run ls *.doc, the shell rewrites it into ls letter.doc notes.doc.

    Runtime.exec(String) doesn't, it just passes them as arguments.

    ls has no idea what * is, so the command fails.

  3. The shell manages pipes and redirections.

    When you run ls mydir > output.txt, the shell opens "output.txt" for command output and removes it from the command line, giving ls mydir.

    Runtime.exec(String) doesn't. It just passes them as arguments.

    ls has no idea what > means, so the command fails.

  4. The shell expands variables and commands

    When you run ls "$HOME" or ls "$(pwd)", the shell rewrites it into ls /home/myuser.

    Runtime.exec(String) doesn't, it just passes them as arguments.

    ls has no idea what $ means, so the command fails.

What can you do instead?

There are two ways to execute arbitrarily complex commands:

Simple and sloppy: delegate to a shell.

You can just use Runtime.exec(String[]) (note the array parameter) and pass your command directly to a shell that can do all the heavy lifting:

// Simple, sloppy fix. May have security and robustness implications
String myFile = "some filename.txt";
String myCommand = "cp -R '" + myFile + "' $HOME 2> errorlog";
Runtime.getRuntime().exec(new String[] { "bash", "-c", myCommand });

Secure and robust: take on the responsibilities of the shell.

This is not a fix that can be mechanically applied, but requires an understanding the Unix execution model, what shells do, and how you can do the same. However, you can get a solid, secure and robust solution by taking the shell out of the picture. This is facilitated by ProcessBuilder.

The command from the previous example that requires someone to handle 1. quotes, 2. variables, and 3. redirections, can be written as:

String myFile = "some filename.txt";
ProcessBuilder builder = new ProcessBuilder(
"cp", "-R", myFile, // We handle word splitting
System.getenv("HOME")); // We handle variables
builder.redirectError( // We set up redirections
ProcessBuilder.Redirect.to(new File("errorlog")));
builder.start();

Runtime.exec not working

If you use the Runtime.exec(String) overload, the string is treated as a command and its arguments, and is crudely split into substrings at white-space boundaries. This splitting is standard behaviour for that overload. (Refer to the javadoc.)

Runtime.exec(...) expects a native command and its arguments. You've provided a line of shell input. The exec methods don't understand shell input and don't know how to execute it properly. And the crude splitting (see above) messes up everything.

If you need to do that, then use the following:

String yourShellInput = "echo hi && echo ho";  // or whatever ... 
String[] commandAndArgs = new String[]{ "/bin/sh", "-c", yourShellInput };
Runtime.getRuntime().exec(commandAndArgs);

This is equivalent to running:

$ /bin/sh -c "echo hi && echo ho".

If sh is not installed as /bin/sh use the path where it is installed instead.

Java Runtime exec() not working

Please note that you don't need xdg-open to do this.
You can use the java platform-agnostic Desktop API:

if(Desktop.isDesktopSupported()) {
Desktop.open("/path/to/file.txt");
}

Update

If the standard approach still gives issues, you can pass the parameters as an array since Runtime.exec does not invoke a shell and therefore does not support or allow quoting or escaping:

String program;
if (Program.isPlatformLinux())
{
program = "xdg-open";
} else {
program = "something else";
}

Runtime.getRuntime().exec(new String[]{program, file.getAbsolutePath()});

Runtime.exec not running but no exception

You can debug or print the Process.getErrorStream and Process.getInputStream to check for an error reported by the sub-process.

My guess is that you need to invoke /bin/sh as the main program and pass your script as an argument, but I'm not sure. I know that you have to do that on windows.

java getRuntime().exec() does not work for running basic cmd commands

I solved this issue by using ProcessBuilder. I still don't know why the earlier code didn't work for all the commands. But by using ProcessBuilder, I was able to perform any cmdquery.

Here's the code for reference:

String command_ping = "ping " + host_name;

try {

ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", command_ping);
builder.redirectErrorStream(true);
Process p = builder.start();

BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuffer buffer = new StringBuffer();
String line = "";
while (true)
{

buffer.append(line).append("\n");
line = r.readLine();
if (line == null) { break; }
}
message_ping= buffer.toString();
p.waitFor();
r.close();

}

catch (IOException e)
{
e.printStackTrace();
}

catch (InterruptedException e)
{
e.printStackTrace();
}

Command works in terminal, but not with Runtime.exec

This is the most common mistake of all times when it comes to a Process.

A process is not a shell interpreter. As such, any special shell "keywords" will not be interpreted.

If you try and exec cmd1 && cmd2, what happens is that the arguments of the process are literally cmd1, &&, cmd2. Don't do that.

What is more, don't use Runtime.exec(). Use a ProcessBuilder instead. Sample code:

final Process p = new ProcessBuilder("cmd1", "arg1", "arg2").start();
final int retval = p.waitFor();

See the javadoc for ProcessBuilder, it has a lot of niceties.

Oh, and if you use Java 7, don't even bother using external commands. Java 7 has Files.copy().

And also, man execve.

getRuntime().exec() does nothing

You need to call the jar with java.exe, and you're not doing that. Also you need to trap the input and error streams from the process, something you can't do the way you're running this. Use ProcessBuilder instead, get your streams and then run the process.

For example (and I can only do a Windows example),

import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class ProcessEg {
private static Process p;

public static void main(String[] args) {
String[] commands = {"cmd", "/c", "dir"};
ProcessBuilder pBuilder = new ProcessBuilder(commands);
pBuilder.redirectErrorStream();

try {
p = pBuilder.start();
InputStream in = p.getInputStream();
final Scanner scanner = new Scanner(in);
new Thread(new Runnable() {
public void run() {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
try {
int result = p.waitFor();
p.destroy();
System.out.println("exit result: " + result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Runtime.exec not running cd

You can't (usefully) issue a cd command through Runtime.exec. The cd command, on most OS's, is a built-in command of the shell, not an executable (which is why you get the error you get), and it operates on the runtime environment of the shell. Although you could use Runtime.exec to fire up a shell and execute the cd command within it (for Windows that would be cmd.exe /c "cd path"), it would only change the current directory within the shell, not for the program running.

What you need to do is resolve the directory within your program, using the various features of File, and use that resolved absolute file path for whatever it is that you're trying to use cd for.

If you post a (new) question saying what it is you're trying to achieve by using cd, we can help you achieve that, but using Runtime.exec to issue cd isn't going to be the solution.

Runtime.exec command not working

I suspect this:

String command = "wget -O " + destFile + " \""+ srcFile +"\"";

is the problem. When you run in a shell, the quotes around the URL will be removed. However when you run via Java you're not running via a shell and your URL starts with "http... (look closely at the error message).

If you don't want Runtime.exec() to parse and split your arguments then you might consider the variant that takes individual arguments. A more efficient solution still would be to download using HttpComponents.



Related Topics



Leave a reply



Submit