Как выполнить /bin/sh с помощью commons-exec?
Это то, что я делаю:
import org.apache.commons.exec.*;
String cmd = "/bin/sh -c \"echo test\"";
new DefaultExecutor().execute(CommandLine.parse(cmd));
Это вывод:
/bin/sh: echo test: command not found
Что я делаю неправильно?
5 ответов
Этот работает для меня:-
CommandLine command = new CommandLine("/bin/sh");
command.addArguments(new String[] {
"-c",
"echo 'test'"
},false);
new DefaultExecutor().execute(command);
Согласно FAQ "Рекомендуется использовать CommandLine.addArgument()
вместо CommandLine.parse()
".
Так что постарайтесь
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);
Я могу воспроизвести вашу проблему из командной строки:
# /bin/sh -c '"echo test"'
# /bin/sh: echo test: command not found
Я бы сказал, что вы должны попытаться не цитировать команду.
Команда не работает, потому что commons-exec делает ненужное цитирование всех аргументов, которые имеют пробел или кавычку.
Это ненужное цитирование аргументов контролируется handleQuoting
флаг каждого аргумента. Если вы создаете CommandLine
объект, используя его конструктор и addArgument
методы, вы можете установить этот флаг false
,
Как это:
CommandLine commandLine = new CommandLine("/bin/sh");
commandLine.addArgument("-c");
commandLine.addArgument("echo test", false);
(Важная часть false
как второй аргумент addArgument
метод)
И это работает! Но... неудобно создавать командную строку вручную, вместо того, чтобы определять ее в каком-либо файле конфигурации.
CommandLine.parse
всегда устанавливает handleQuoting
признак истины! Кто знает почему...
Я написал этот маленький вспомогательный метод, используя отражение, чтобы исправить "плохо" CommandLine
объект, созданный с использованием 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;
}
}
Это просто клонирует данное CommandLine
, устанавливая каждый аргумент handleQuoting
флаг для false
, Метод FieldUtils.readField
Из библиотеки commons-lang3, но вы можете использовать простое отражение, если хотите.
Это позволяет анализировать командную строку и при этом успешно ее выполнять.
String cmd = "/bin/sh -c 'echo test'";
new DefaultExecutor().execute(fixCommandLine(CommandLine.parse(cmd)));
import org.apache.commons.exec.*;
CommandLine commandLine = new CommandLine("abc.sh");
commandLine.addArgument("param1");
final Executor exec = new DefaultExecutor().execute(commandLine);