views:

193

answers:

2

I am currently using ProcessBuilder to run commands from a java server. This server is to replace an old Perl server, and a lot of our legacy code specifies platform specific command lines.

For instance, on windows it might do:

command -option "hello world"

and on unix it might do:

command -option 'hello world'

The problem is that ProcessBuilder and Runtime.exec both take in tokenized command lines (e.g., {"command", "-option", "hello world"} for both unix and windows).

While I prefer the platform independant way, we have somewhere in the range of 30 million lines of perl code in our codebase. Without me writing a tokenizer for the different platforms (not a big deal really, I just don't want to make a WTF), is there a way to let the shell on the operating system tokenize the command line?

+3  A: 

Are you able to use the overloaded Runtime.exec(String) that takes a single String and runs that as the entire command?


The following works for me on Windows:

Process p = Runtime.getRuntime().exec("perl -e \"print 5\"");
System.out.println(IOUtils.toString(p.getInputStream()));
p.destroy();

This is more or less what Runtime.exec(String) is doing:

public static void main(String [] args) throws Exception {
    Process p = new ProcessBuilder(getCommand("perl -e \"print 5\"")).start();
    System.out.println(IOUtils.toString(p.getInputStream()));
    p.destroy();

}

private static String[] getCommand(String input) {
    StringTokenizer tokenizer = new StringTokenizer(input);
    String[] result = new String[tokenizer.countTokens()];
    for (int i = 0; tokenizer.hasMoreTokens(); i++) {
        result[i] = tokenizer.nextToken();
    }
    return result;
}
Kevin
Is Perl on the path for the environment that the process is executing in?
Kevin
Oops, I just realized that I was using ProcessBuilder, not Runtime.exec().
tster
Is there anyway to use Runtime.exec() without loosing the ability to redirect stderr to stdout?
tster
It doesn't appear so. However, if you look under the covers for Runtime.exec(String), you'll see it is simply using a StringTokenizer to parse the arguments into an array and then passing that to ProcessBuilder. Perhaps you could do something similar
Kevin
Thanks for your help Kevin.
tster
A: 

I think the quotes are being interpreted by the shell. Instead, put the command in a shell script:

$ cat temp.sh 
#!/bin/sh
perl -e "print 5"

and execute it:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class PBTest {

    public static void main(String[] args) {
        ProcessBuilder pb = new ProcessBuilder("./temp.sh");
        pb.redirectErrorStream(true);
        try {
            Process p = pb.start();
            String s;
            BufferedReader stdout = new BufferedReader (
                new InputStreamReader(p.getInputStream()));
            while ((s = stdout.readLine()) != null) {
                System.out.println(s);
            }
            System.out.println("Exit value: " + p.waitFor());
            p.getInputStream().close();
            p.getOutputStream().close();
            p.getErrorStream().close();
         } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Console:

5
Exit value: 0
trashgod