Дочерний процесс Windows Java не вводится и не выводится, если установлен стандартный родительский ввод-вывод (командная строка)

В Windows я не могу надежно манипулировать своим дочерним процессом & apos; Ввод / вывод, когда моя программа была запущена из командной строки. Это разочаровывает, поскольку для серверов является стандартным использование консоли для ввода / вывода. Графические интерфейсы хороши, но я действительно предпочитаю придерживаться командной строки и сохранять простоту. Я заметил, что ввод-вывод дочерних процессов в порядке, когда я выполняю свой сервер из Eclipse IDE, но это совсем другая история, запускаемая из командной строки. Я не могу читать или писать в дочерний процесс, но процесс все равно будет запущен. Ниже я написал некоторый тестовый код, который демонстрирует эту проблему, и я надеюсь, что проблема может быть воспроизведена на другом компьютере, а затем, надеюсь, получит решение из нее. При выполнении из Eclipse унаследованный ввод-вывод работает как положено. Однако при выполнении из командной строки Windows ничто не может быть прочитано или записано в дочерний процесс. В обоих случаях перенаправление вывода дочернего процесса в файл всегда завершается успешно, но ввод по-прежнему не может быть передан дочернему процессу. Если уже есть решение этой проблемы, пожалуйста, перейдите по ссылке на страницу.

Реализация JRE / JDK:

>java -version
java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode)

Рассмотрим следующий код:

package com.comp8nerd4u2.io.test;

/*
 * These tests attempt to confirm what I'm experiencing under my build environment
 */

import java.io.File;
import java.io.IOException;

public final class PIOTest {

/** The command to run as a child process. The command itself isn't the test, but what you use to run this Java program is the test. */
private static final String[] COMMAND = {"cmd.exe", "/c", "echo This is a test. Feel free to change this."}; // Change this to just {"cmd.exe"} or some other program that accepts input and you'll see how frustrating this is
/** Controls how the test process is built */
private static final ProcessBuilder PB = new ProcessBuilder(COMMAND);
/** How long to allow the process to run before forcibly terminating it. */
private static final long PROCESS_TIMEOUT = 10000L;
private static final Runnable R = new TimedInterruptWorker(PROCESS_TIMEOUT);

private static int n = 0;

static {
    PB.redirectErrorStream(true);
}

private PIOTest() {}

public static void main(String[] args) {

    // ----- Begin Tests -----

    /*
     * Test #1: Let's test putting our command's output onto our standard I/O streams
     * Goal condition: Child process outputs expected output, and exits before the timeout. If child process expects input, it should accept entered input.
     * Known success factors: Parent process' standard I/O is piped to Eclipse. Tests would probably succeed with Netbeans as well
     * Known fail factors: Parent process' standard I/O is piped to Windows Command Prompt
     * Result under fail condition: Child process hangs if it fills up its output buffer or requests input, but exits on its own otherwise, unless it took longer than the timeout. 
     */
    PB.inheritIO();
    doTest();

    // Test #2: Let's test putting our command's output into a file
    PB.redirectOutput(new File("piotest.txt"));
    doTest();
}

/**
 * Performs the I/O test.
 */
private static void doTest() {
    n++;
    Process p = null;
    try {
        p = PB.start();
    } catch (IOException e) {
        e.printStackTrace();
        return;
    }
    try {
        Thread t = new Thread(R);
        t.setDaemon(true);
        t.start();
        System.out.format("[Test #%d] Child exited with status code %d\n", n, p.waitFor());
        t.interrupt();
    } catch (InterruptedException e) {
        p.destroy();
        System.out.format("[Test #%d] Child took longer than the timeout.\n", n);
    }
}

/**
 * Useful for sending interrupts after a certain amount of time has passed.
 * 
 * @author comp8nerd4u2
 */
private static final class TimedInterruptWorker implements Runnable {

    private long timeout = 0;
    private Thread target = null;

    public TimedInterruptWorker(long timeout) {
        this(timeout, Thread.currentThread());
    }

    public TimedInterruptWorker(long timeout, Thread target) {
        this.timeout = timeout;
        this.target = target;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException e) {
            return;
        }
        target.interrupt();
    }

}

}

ОБНОВЛЕНИЕ: я изменил тест, чтобы принимать любую команду во время выполнения, и загрузил ее на мой Linux-сервер VPS. Я запустил его из сеанса ssh и всех дочерних процессов & apos; Ввод / вывод можно легко прочитать и записать. Была одна вещь, которую я заметил. Когда я открыл интерактивную оболочку bash как дочерний процесс, а затем перенаправил ее вывод в файл, я думаю, что CentOS остановил мою программу. Это или моя программа потерпела крах.

[[email protected] piotest]$ java -jar piotest.jar
Enter command to run : bash
[[email protected] piotest]$ [Test #1] Child took longer than the timeout.

[1]+  Stopped                 java -jar piotest.jar
[[email protected] piotest]$

Первая строка - мой ввод в команде. Вторая строка - это оболочка bash, которая была создана, но я никогда ничего не вводил в нее, поэтому моя программа убивает ее после истечения времени ожидания. Он готовится ко второму тесту, создает & quot; piotest.txt & quot; файл, а затем либо вылетает, либо останавливается ОС. Сам фактический тест не изменился, за исключением того, что теперь тест позволяет вам вводить какую команду запускать во время выполнения. Это прекрасно работает в Linux, но не в Windows. Я надеюсь, что кто-то, кто знает Win32 API, может как-то объяснить, почему этот тест не проходит в Windows.

Ответы на вопрос(2)

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

На самом деле это известная ошибка для Windows:https://bugs.openjdk.java.net/browse/JDK-8023130

Вы можете обойти это, перенаправив потоки самостоятельно:

Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
   System.out.println(line);
}

p.waitFor();

br.close();

Вы видели эту статью?http://www.javaworld.com/jw-12-2000/jw-1229-traps.html?page=1

Мне кажется, что вам нужно обслуживать потоки ввода / вывода в Windows. В статье рассказывается о Runtime.exec, но я уверен, что нативный код для ProcessBuilder очень похож и имеет те же проблемы в Windows.

Я предполагаю, почему это работает в Eclipse в Windows, что Eclipse обслуживает потоки от вашего имени, чтобы отображать вещи в представлении консоли.

Ваш ответ на вопрос