3

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?

yegor256
  • 102,010
  • 123
  • 446
  • 597

5 Answers5

8

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);
Christoffer Hammarström
  • 27,242
  • 4
  • 49
  • 58
3

This one works for me:-

    CommandLine command = new CommandLine("/bin/sh");
    command.addArguments(new String[] {
            "-c",
            "echo 'test'"
    },false);
    new DefaultExecutor().execute(command);
limc
  • 39,366
  • 20
  • 100
  • 145
  • Does not do the same thing as in the question. This would execute `/bin/echo` with the argument `test` instead of `/bin/sh` with the arguments `-c` and `echo test`. They are different because `echo` is also built into `/bin/sh`. – Christoffer Hammarström Feb 22 '11 at 15:51
  • @limc it works, but it's not the point to make this particular example working. The point is to provide a complex argument into external process, which I can't do so far.. – yegor256 Feb 22 '11 at 15:56
  • @yegor256: Did you look at my corrected version before you marked me down? – limc Feb 22 '11 at 15:57
  • @yegor256: Wait, what complex argument? Are you trying to do something beyond `echo test` ? – Christoffer Hammarström Feb 22 '11 at 16:00
  • When calling `command.addArguments()`, set the quote handling to false. – limc Feb 22 '11 at 16:01
  • @limc that's the solution (`,false` argument), thanks! Now it works. I can't remove the down-vote, unless you edit your answer a bit. Please do this and I will upvote instead. – yegor256 Feb 22 '11 at 16:07
  • 3
    I just spent some time reading through the source of commons-exec, and it appears that it goes full retard if you don't provide `false` for the `handleQuoting` parameter wherever possible. Really awful design, IMHO. – Christoffer Hammarström Feb 22 '11 at 16:36
0
import org.apache.commons.exec.*; 

CommandLine commandLine = new CommandLine("abc.sh");
commandLine.addArgument("param1"); 
final Executor exec = new DefaultExecutor().execute(commandLine);
Ram Ghadiyaram
  • 28,239
  • 13
  • 95
  • 121
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. – Nic3500 Sep 05 '18 at 00:32
0

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)));
Ruslan Stelmachenko
  • 4,987
  • 2
  • 36
  • 51
0

I can reproduce your problem from a shell command line:

# /bin/sh -c '"echo test"'
# /bin/sh: echo test: command not found

I'd say you should try to not quote the command.

Kevin
  • 24,871
  • 19
  • 102
  • 158