views:

1468

answers:

7

I had some code that ran commands through Runtime.getRuntime.exec(String), and it worked on Windows. When I moved the code to Linux, it broke, and the only way of fixing it was to switch to the exec(String[]) version. If I leave things this way, will the code work the same on Windows and Linux, or should I use the exec(String) on Windows and exec(String[]) on Linux?

+1  A: 

From [the api][1]

This method checks that cmdarray is a valid operating system command. Which commands are valid is system-dependent, but at the very least the command must be a non-empty list of non-null strings.

So yes, it is system-dependent. BTW, could you post the relevant code, so we can see what you're (eventually) do wrong?

[1]: http://java.sun.com/javase/6/docs/api/java/lang/Runtime.html#exec(java.lang.String[], java.lang.String[], java.io.File)

akappa
Nice, in the preview the link isn't broken!
akappa
+2  A: 

Okay, I give up: What non-trivial command can you pass to exec(), and expect to get reasonable results on both Windows and Linux? Asking whether exec() is platform independent kind of seems to be missing the whole point of exec(), which is to invoke platform-specific behavior.

To actually address your question, yes - the way that the command string is interpreted will be different on different platforms (and potentially, for different shells, on Linux), and using the String[] version is more likely to end up with the parameters getting passed correctly.

Mark Bessey
I was trying to execute javac for all the .java files inside a project, and include jar dependencies as well.
Geo
Ahh, I should have guessed - running JDK commands is a reasonable thing to want to do from a Java application.
Mark Bessey
@Mark. Not that much actually. Arguments on both sides are different, like path / and path separetor : vs ; Drives etc. so the commands won't really wok transparently from one platform to the other.
OscarRyz
@Geo: There is since java 1.6 I think an API for the java compiler. I think is something like java.lang.Compiler, it was added precisely to avoid these kinds of problems and allow containers such as Servlet containers, compile the generated .java file from within the application rather than by external process.
OscarRyz
+2  A: 

The default Tokeniser for splitting the exec(String) parameter into a String[] simply splits them by the space character. It does not interpret quotes like a manually entered shell command would, so you should call the String[] version. But if you are using the Sun JDK on both platforms, the behaviour should be similar.

However, as Windows provides different shell commands as other Operating Systems (for example copy instead of cp), your commands might not work on all platforms.

arturh
+1  A: 

exec() invokes native commands on the underlying operating system.

Commands designed for Windows will not work on Linux, and will have to be rewritten.

Thorbjørn Ravn Andersen
+4  A: 

Use String[] on both.

The answer I gave you before was the result of several miserable hours of debugging a production software running on windows.

After a lot of effort we ( I ) came to the solution posted before ( use String[] )

Since the problems you've got were on Linux, I guess using the array on both will be the best.

BTW. I tried that method with Java 1.4, Since then a new class is available: ProcessBuilder added on Java1.5. I'm not sure what is that all about, but there should be a good reason for it. Take a look at it and read what the difference between that an Runtime.exec is. Probably it will be a better option.

Finally some commands won't work on either platform because they are built in with the shell ( either Windows cmd or bash/sh/ etc ) such as dir or echo and some of those. So I would recommend do extra/extra test on each target platform and add exception handlers for the unsupported commands.

:)

OscarRyz
It worked on both operating systems. Thanks.
Geo
+2  A: 

The difference between the the exec() method that takes a single string, and the one that accepts an array is that the array version allows you to specify how the command and its arguments need to be broken up (ie. correctly). When you use the method(s) that accept just a string the method breaks it up into an array on whitespace. Processing from then on is handled the same for both methods.

This is the code from Runtime class that shows how it breaks single string into an array and then calls String[] version to continue processing.

   public Process exec(String command, String[] envp, File dir)
            throws IOException {
         if (command.length() == 0)
             throw new IllegalArgumentException("Empty command");

         StringTokenizer st = new StringTokenizer(command);
         String[] cmdarray = new String[st.countTokens()];
         for (int i = 0; st.hasMoreTokens(); i++)
             cmdarray[i] = st.nextToken();
         return exec(cmdarray, envp, dir);
     }

So the method that accepts a String will not work if breaking the command on whitspace does not separate command and arguments correctly.

objects
+1  A: 

If you want to run different commands on Windows and Linux you could find out which OS is running by using something like the following:

        String os = System.getProperty("os.name").toLowerCase();
        if (os.indexOf("win") >= 0) {
            // Windows Commands
        } else {
            // Linux Commands
        }

Another good thing would be to write you're commands in a script (.bat for Windows and .sh for Linux) and call these scripts instead.

rohit.arondekar