Как выполнить /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);
Другие вопросы по тегам