This is what I'm doing:
import org.apache.commons.exec.*;
String cmd = "/bin/sh -c \"echo test\"";
new DefaultExecutor().execute(CommandLine.parse(cmd));
This is the output:
/bin/sh: echo test: command not found
What am I doing wrong?
This is what I'm doing:
import org.apache.commons.exec.*;
String cmd = "/bin/sh -c \"echo test\"";
new DefaultExecutor().execute(CommandLine.parse(cmd));
This is the output:
/bin/sh: echo test: command not found
What am I doing wrong?
According to the FAQ "It is recommended to use CommandLine.addArgument()
instead of CommandLine.parse()
".
So try
CommandLine commandLine = new CommandLine("/bin/sh");
commandLine.addArgument("-c");
commandLine.addArgument("echo test", false); // false is important to prevent commons-exec from acting stupid
executor.execute(commandLine);
The command doesn't work because commons-exec doing unnecesary quoting of all arguments, which have space or quote.
This unnecessary quoting of arguments is controlled by handleQuoting
flag of each argument. If you create the CommandLine
object using it's constructor and addArgument
methods, you can set this flag to false
.
Like this:
CommandLine commandLine = new CommandLine("/bin/sh");
commandLine.addArgument("-c");
commandLine.addArgument("echo test", false);
(The important part is false
as second argument of addArgument
method)
And it works! But... it is unconvinient to construct command line manually instead of having it defined in some config file.
CommandLine.parse
always sets the handleQuoting
flag to true! Who knows why...
I wrote this small helper method using reflection to fix "bad" CommandLine
object, created using CommandLine.parse
.
public static CommandLine fixCommandLine(CommandLine badCommandLine) {
try {
CommandLine fixedCommandLine = new CommandLine(badCommandLine.getExecutable());
fixedCommandLine.setSubstitutionMap(badCommandLine.getSubstitutionMap());
Vector<?> arguments = (Vector<?>) FieldUtils.readField(badCommandLine, "arguments", true);
arguments.stream()
.map(badArgument -> {
try {
return (String) FieldUtils.readField(badArgument, "value", true);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
})
.forEach(goodArgument -> fixedCommandLine.addArgument(goodArgument, false));
return fixedCommandLine;
} catch (Exception e) {
logger.error("Cannot fix bad command line", e);
return badCommandLine;
}
}
It just clones the given CommandLine
, setting each argument's handleQuoting
flag to false
.
The method FieldUtils.readField
is from commons-lang3 library, but you can use plain reflection if you want.
It allows to parse command line and still successfully execute it.
String cmd = "/bin/sh -c 'echo test'";
new DefaultExecutor().execute(fixCommandLine(CommandLine.parse(cmd)));
This one works for me:-