Классическая проблема с выполнением внешних команд в Java? - PullRequest
1 голос
/ 05 марта 2011

Ладно. Этот вопрос представляет собой проблему для многих людей. Так как я еще не нашел ответ, который работает, я подумал, что смогу выразить эту проблему, чтобы кто-то, кто понял это, мог рассказать остальным .

Проблема в том, что два из трех приведенных ниже работают просто с одинаковым кодом.

Пример для reader3 демонстрирует проблему. Reader3 не может прочитать результат успешного запуска внешнего файла. Попытка выполнить любой тип чтения (realine и т. Д.) В блоках InputStream в stdin или stderr навсегда:

package Problems;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RunningProblem {

public static class RunningReader implements Runnable {

    private Process proc;
    private String sName;

    private RunningReader(Process proc1, String sName) {
        this.proc = proc1;
        this.sName = sName;
    }

    public void run() {
        try {                
            // InputStreamReader in = new InputStreamReader(proc.getInputStream());
            // BufferedReader reader = new BufferedReader(in);

            InputStreamReader err = new InputStreamReader(proc.getErrorStream());
            BufferedReader reader = new BufferedReader(err);

            String line = reader.readLine();
            while (line != null) {
                System.out.println(sName + ": " + line);
                line = reader.readLine();
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) {
    ExecutorService pool = Executors.newFixedThreadPool(3);
    try {
        Runtime rt = Runtime.getRuntime();

        Process proc1 = rt.exec("ps ax");
        RunningReader reader1 = new RunningReader(proc1, "reader1");

        Process proc2 = rt.exec("ls -l /");
        RunningReader reader2 = new RunningReader(proc2, "reader2");

        Process proc3 = rt.exec("/bin/tar");
        RunningReader reader3 = new RunningReader(proc3, "reader3");

        pool.execute(reader3);
        pool.execute(reader2);
        pool.execute(reader1);

    } catch (Exception ex) {
        System.err.println(ex.getMessage());
    } finally {
        pool.shutdown();
    }
    System.out.println("Launcher.main() Exited.");
}

}

Ответы [ 4 ]

1 голос
/ 05 марта 2011

Вы не показывали вывод, который выдает ваша программа, но я думаю, что это может быть тот случай, когда ps ax и ls -l / не выдают вывод, а /bin/tar -. Причина в том, что первые две команды выводят на stdout, но не stderr, тогда как последняя выдаст на stderr (поскольку вы не даете действительные параметры tar), но не на stdout .

Вот разница при запуске команд в оболочке:

[axe@gromp tmp]$ ps ax > ps-std.txt 2> ps-err.txt
[axe@gromp tmp]$ ls -l / > ls-std.txt 2> ls-err.txt
[axe@gromp tmp]$ /bin/tar > tar-std.txt 2> tar-err.txt
[axe@gromp tmp]$ ls -lrt
total 18
-rw-r--r-- 1 axe users    0 Mar  5 19:40 ps-err.txt
-rw-r--r-- 1 axe users 7191 Mar  5 19:40 ps-std.txt
-rw-r--r-- 1 axe users    0 Mar  5 19:40 ls-err.txt
-rw-r--r-- 1 axe users  937 Mar  5 19:40 ls-std.txt
-rw-r--r-- 1 axe users    0 Mar  5 19:41 tar-std.txt
-rw-r--r-- 1 axe users  142 Mar  5 19:41 tar-err.txt
[axe@gromp tmp]$ 

Используя > для перенаправления стандартного вывода и 2> для перенаправления вывода ошибки в разные файлы, вы можете видеть, что tar создает сообщения на stderr, а два других на stdout (размеры файлов для остальные файлы равны нулю, выходных данных не было).

Может быть, это так? Что произойдет, если вы запустите, e. г. echo "Foo" вместо tar в качестве третьего процесса?

1 голос
/ 05 марта 2011

Я запустил ваш код в моей системе, и он выдал мне следующий вывод перед обычным выходом:

reader3: /bin/tar: You must specify one of the `-Acdtrux' options
reader3: Try `/bin/tar --help' or `/bin/tar --usage' for more information.
Launcher.main() Exited.

Не было ни вывода из ps ax, ни ls -l /, но запуск их из оболочки подтверждает, что они ничего не записали в стандартную ошибку. В моей системе ваш код завершился нормально, но я могу представить себе ситуации, когда этого не происходит. Имейте в виду, что если процесс генерирует много выходных данных на своем стандартном выводе, буфер может заполниться, что приведет к зависанию процесса.

Я бы рекомендовал использовать ProcessBuilder вместо Runtime.getRuntime().exec("..."). Во-первых, он позволяет перенаправить стандартный поток ошибок в стандартный поток вывода, а затем не беспокоиться о том, с какого из двух потоков читать.

0 голосов
/ 06 марта 2011

Facinating (спокан брови) - судя по вышеприведенным комментариям, кажется, что есть веские причины, по которым у многих людей возникают проблемы с этим - каждая из наших реализаций работает совершенно по-разному!

Я использую Ubuntu. Интересно, что использование ProcessBuilder перевернуло проблему ... но, по крайней мере, ни один из них теперь не блокируется навсегда при его использовании. - По крайней мере, stderr и stdin можно было прочитать!

Так что для меня, на данный момент, эмпирическое правило выглядит следующим образом: используйте «старый способ» (Runtime.getRuntime (). Exec ()) для команд командной оболочки («внутренних») в Ubuntu (Oracle / Sun VM) - используйте ProcessBuilder для внешних команд (таких как tar и т. Д.):

Process proc3 = new ProcessBuilder("/bin/tar").start();
RunningReader reader3 = new RunningReader(proc3, "reader3");

...

reader3: / bin / tar: Вы должны указать один из -Acdtrux' options reader3: Try / bin / tar --help 'или `/ bin / tar --usage 'для получения дополнительной информации.

- Это очень важная операция для многих из нас ... Было бы неплохо составить матрицу для использования на какой платформе ....? (т.е. мне интересно, будет ли OpenJDK лучше работать в Ubuntu?)

0 голосов
/ 05 марта 2011

Вероятно, вы запустили RunningReaders слишком поздно.Вы запускаете три процесса, и, вероятно, первый из них иногда завершается, прежде чем вы начнете читать Error-OutputStream в вашем RunningReader.

RunningReaders должен быть запущен сразу после запуска процесса.При определенных условиях даже этого может быть недостаточно.Затем вам нужно будет создать скрипт-обертку (для каждой ОС), который захватывает выходные данные и записывает их в файл.

...