1

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?

tster
  • 17,883
  • 5
  • 53
  • 72
  • `command -option "hello world"` works on unix as well, quotes are quotes... – akuhn Dec 30 '09 at 15:21
  • Regardless, our code does conditionalize on windows vs. unix, usually because the commands are more complex than that and have nested quotes and command line variables and other such nonsense. – tster Dec 30 '09 at 15:35
  • 30 million lines of perl?!? I'm so glad I'm not you ;) – Fantius Jun 28 '12 at 20:42

2 Answers2

7

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
  • 30,111
  • 9
  • 76
  • 83
  • Is Perl on the path for the environment that the process is executing in? – Kevin Dec 30 '09 at 15:35
  • Oops, I just realized that I was using ProcessBuilder, not Runtime.exec(). – tster Dec 30 '09 at 15:37
  • Is there anyway to use Runtime.exec() without loosing the ability to redirect stderr to stdout? – tster Dec 30 '09 at 15:43
  • 1
    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 Dec 30 '09 at 15:50
5

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
  • 203,806
  • 29
  • 246
  • 1,045