Runtime.getRuntime (). Exec ("C: \ cygwin \ bin \ bash.exe") не имеет данных для чтения - PullRequest
2 голосов
/ 04 сентября 2010

Я пытаюсь выполнить новый процесс и прочитать его входной поток в Java.Я успешно использовал Runtime.getRuntime (). Exec (String) для запуска и получения входных данных от нескольких процессов.Однако, когда я пытаюсь использовать exec в некоторых других процессах, метод чтения входного потока блокируется, и кажется, что нет ввода.Что может быть причиной того, что входной поток будет пустым для некоторых из этих процессов?В частности, мне интересно, почему bash.exe ничего не выводит.

Я написал тестовый пример JUnit для демонстрации этой проблемы:

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import junit.framework.TestCase;

public class TestExec extends TestCase {

    public void testExec() throws IOException {
        List<InputPrinter> threads = new ArrayList<InputPrinter>();

        // Create a process for each of the commands and make sure that
        // it outputs at least one line to its input stream.
        threads.add(testExec("cmd"));
        threads.add(testExec("java"));
        threads.add(testExec("C:/cygwin/bin/vim-nox.exe"));

        // These bottom two fail, even though executing these
        // commands in cmd.exe results in immediate output
        threads.add(testExec("javac"));
        threads.add(testExec("C:/cygwin/bin/bash.exe"));

        // Give the threads a second to execute
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            fail();
        }

        // Test that each command had input to read
        for(InputPrinter ip : threads) {
            assertTrue(ip.command + " has not read any input", ip.hasRead);
        }
    }

    // Starts a process for the given command and returns an
    // InputPrinter that can be used to check if the process
    // has had an input to read.
    public InputPrinter testExec(String command) throws IOException {
        Process proc = Runtime.getRuntime().exec(command);
        InputStream in = proc.getInputStream();

        InputPrinter ip = new InputPrinter(in, command);
        new Thread(ip).start();

        return ip;
    }

    // Simple Runnable to read from an InputStream. hasRead will be
    // true if at least one input has been read from the stream
    private class InputPrinter implements Runnable {
        InputStream in;
        String command;
        boolean hasRead;

        public InputPrinter(InputStream in, String command) {
            this.in = in;
            this.command = command;
            this.hasRead = false;
        }

        // Loop indefinitely while printing any received input
        public void run() {
            try {
                final byte[] b = new byte[1024];
                while (true) {
                    int n = in.read(b);
                    if (n > 0) {
                        System.out.print(new String(Arrays.copyOf(b, n)));
                        hasRead = true;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                fail();
            }
        }
    }

}

РЕДАКТИРОВАТЬ:

КакНасколько я знаю, если программа не использует stdout или stderr, я не должен ничего видеть в командной строке Windows.Когда я запускаю процесс bash, я ожидаю увидеть то же самое, что и при открытии командной строки и запуске bash.exe:

Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\cygwin\bin>bash.exe
bash-3.2$

Ответы [ 2 ]

2 голосов
/ 04 сентября 2010

С процессом обычно связан не только один, но и два выходных потока. Это:

  1. стандартный вывод, который можно прочитать с помощью getInputStream ()
  2. stderr, который можно прочитать с помощью getErrorStream ()

Javac пишет в stderr, а не в stdout, поэтому вы не читаете его вывод.

Поскольку читать их оба неудобно (несколько лет назад мне пришлось написать дополнительный поток для этого), они представили новый API для системных процессов, а именно ProcessBuilder, который позволяет перенаправлять stderr в stdout .

Просто замените строки

    Process proc = Runtime.getRuntime().exec(command);
    InputStream in = proc.getInputStream();

с

    ProcessBuilder pb = new ProcessBuilder(command);
    pb.redirectErrorStream(true);
    Process proc = pb.start();

, добавьте необходимый импорт, и ваш тест пройдет успешно:).

2 голосов
/ 04 сентября 2010

Независимо от Java, насколько я знаю, вы можете направлять вывод (или ввод) из / в bash только тогда, когда он работает как скрипт, а не когда он работает как интерактивная оболочка (в этом случае вы можете передать толькопараметры cmd к нему).

Другими словами, когда вы запускаете bash из cmd, как вы упомянули в комментарии, вы видите выходные данные, но они содержатся в процессе bash, это не вывод, который отправляет bashв родительский процесс cmd.

Что касается процесса javac, он фактически отправляет вывод в поток ошибок.Попробуйте запустить из cmd javac 1>null и javac 2>null, и вы увидите разницу.
Вы смотрели на API здесь ?Вы можете попытаться использовать ProcessBuilder и перенаправить поток ошибок обратно в первичный входной поток, так будет намного проще работать с процессами.

...