Search code examples
javasystemexecprocessbuilder

Java String execution in Linux terminal


I am using StringBuilder to create a string and then trying to execute the string on Linux terminal. But instead of executing whole command, it executed half command and then terminates it. This is my java code snippet:

moteCommand.append("CFLAGS+=-DCC2420_DEF_CHANNEL=1");
moteCommand.append(" ");
moteCommand.append("make telosb install.");
moteCommand.append(moteIdList.get(i).toString());
moteCommand.append(" bsl,");
moteCommand.append(moteAddrList.get(i).toString());
String moteCommand2 = moteCommand.toString();
Process moteProgProcess = Runtime.getRuntime().exec(moteCommand2, null,"/opt/tinyos-2.x/apps/XXX/);

It gives me this error: Cannot run program "CFLAGS+=-DCC2420_DEF_CHANNEL=1" (in directory "/opt/tinyos-2.x/apps/xxx"): java.io.IOException: error=2, No such file or directory

I don't understand why system process is trying to execute only half of the string. Please let me know if anybody knows the reason.

Thanks.


Solution

  • When you call Runtime.exec(), the characters up to the first space must be the name of the program you want to launch. After that, each "part" between spaces is a separate argument. Note that calling Runtime.exec() is completely different from typing a command in bash (or any other shell...) and pressing enter!! If you type a command that works fine in bash, it doesn't mean it will work with Runtime.exec(). For example, shell commands (which are not external programs) won't work in Runtime.exec().

    What you should do is use ProcessBuilder.

    Instantiate it, manipulate its Map that represents the environment options (ie, the things you are passing before the command name, such as the cflags, and anything else you might want), set the command name, give the arguments one at a time (the arguments won't get split at spaces, so you can pass paths containing spaces, for instance), etc. You can manipulate the stdin, stdout and stderr in many different ways (such as: use the same as those used by the Java process; or get instances of InputStream and OutputStream to write to and read from the process; or pipe them), and run the process.

    Something along the lines:

    final ProcessBuilder pb = new ProcessBuilder("make", "telosb", "install" blablablabla);
    final Map<String, String> env = pb.environment();
    env.put("CFLAGS", "....your options....");
    pb.start(); // take the Process instance, and you will be able to read the output, wait for it to finish, get the exit code, etc