Исключение java.lang.Runtime "Не удается запустить программу"

Я получаю исключение как java.io.IOException: Cannot run program cat /home/talha/* | grep -c TEXT_TO_SEARCH": error=2, No such file or directory при выполнении команды ниже, несмотря на то, что нет никаких проблем, когда я выполняю ту же команду через терминал. Мне нужно выполнить и вернуть вывод команды ниже:

cat /home/talha/* | grep -c TEXT_TO_SEARCH

Вот метод, используемый для выполнения команд с использованием Runtime учебный класс:

public static String executeCommand(String command) {

    StringBuffer output = new StringBuffer();

    Process p;
    try {
        p = Runtime.getRuntime().exec(command);
        p.waitFor();
        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));

        String line = "";
        while ((line = reader.readLine()) != null) {
            output.append(line + "\n");
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return output.toString();
}

2 ответа

Решение

Runtime.exec не использует оболочку (как, скажем, /bin/bash); он передает команду непосредственно операционной системе. Это означает, что подстановочные знаки, такие как * и трубы (|) не поймут, т.к. cat (как и все команды Unix) не выполняет разбор этих символов. Вам нужно использовать что-то вроде

p = new ProcessBuilder("bash", "-c", command).start();

или, если по какой-то причудливой причине вам необходимо придерживаться устаревших методов Runtime.exec:

p = Runtime.getRuntime().exec(new String[] { "bash", "-c", command });

Если вы только запускаете эту команду cat / grep, вам следует рассмотреть возможность отказа от использования внешнего процесса, поскольку код Java может легко проходить по каталогу, читать строки из каждого файла и сопоставлять их с регулярным выражением:

Pattern pattern = Pattern.compile("TEXT_TO_SEARCH");
Charset charset = Charset.defaultCharset();

long count = 0;

try (DirectoryStream<Path> dir =
    Files.newDirectoryStream(Paths.get("/home/talha"))) {

    for (Path file : dir) {
        count += Files.lines(file, charset).filter(pattern.asPredicate()).count();
    }
}

Обновление: чтобы рекурсивно прочитать все файлы в дереве, используйте Files.walk:

try (Stream<Path> tree =
    Files.walk(Paths.get("/home/talha")).filter(Files::isReadable)) {

    Iterator<Path> i = tree.iterator();
    while (i.hasNext()) {
        Path file = i.next();
        try (Stream<String> lines = Files.lines(file, charset)) {
            count += lines.filter(pattern.asPredicate()).count();
        }
    };
}

$PATH переменная окружения, которая сообщает системе, где искать исполняемые программы (это список каталогов, разделенных двоеточиями). Это обычно устанавливается в вашем .bashrc или же .cshrc файл, но он загружается только при входе в систему. Когда Java работает, $PATH скорее всего, не установлен, потому что rc Файл не выполняется автоматически, поэтому система не может найти программы, не указав точно, где они находятся. Попробуйте использовать /bin/cat или же /usr/bin/cat вместо просто cat и посмотреть, если это работает. Если это так, $PATH это твоя проблема. Можете добавить $PATH=/bin:/usr/bin к вашему сценарию или просто оставьте его с указанным именем каталога (например, /bin/cat).

То, что вы можете выполнить его в сеансе входа в систему, не означает, что он будет работать так же, когда работает демон, подобный вашей Java-программе. Вы должны знать, что в вашем .bashrc или же .cshrc файл и даже иногда как системный файл пишется (/etc/bashrc) чтобы узнать, как написать скрипт, который запускается под демоном. Другое соображение заключается в том, что демоны часто запускаются в контексте другого пользователя, и это тоже отбрасывает вещи.

Другие вопросы по тегам