Untergeordneter Windows Java-Prozess wird nicht eingegeben oder ausgegeben, wenn er auf die Standard-E / A des übergeordneten Computers eingestellt ist (Eingabeaufforderung)

Unter Windows kann ich die E / A meines untergeordneten Prozesses nicht zuverlässig manipulieren, wenn mein Programm über die Befehlszeile gestartet wurde. Es ist frustrierend, da Server standardmäßig eine Konsole für die E / A verwenden. GUIs sind nett, aber ich würde es wirklich vorziehen, mich an die Befehlszeile zu halten und die Dinge einfach zu halten. Ich habe festgestellt, dass untergeordnete Prozess-E / A-Vorgänge in Ordnung sind, wenn ich meinen Server über die Eclipse-IDE ausführe, aber es ist eine ganz andere Geschichte, die über die Befehlszeile ausgeführt wird. Ich kann den untergeordneten Prozess weder lesen noch schreiben, aber der Prozess wird weiterhin ausgeführt. Ich habe unten einen Testcode geschrieben, der dieses Problem demonstriert, und ich hoffe, dass das Problem auf einem anderen Computer reproduziert werden kann, und dann hoffentlich eine Lösung finden kann. Bei der Ausführung von Eclipse funktioniert die geerbte E / A wie erwartet. Bei der Ausführung über die Windows-Eingabeaufforderung kann jedoch nichts in den untergeordneten Prozess gelesen oder geschrieben werden. In beiden Fällen ist die Umleitung der Ausgabe des untergeordneten Prozesses in eine Datei immer erfolgreich, die Eingabe kann jedoch weiterhin nicht an das untergeordnete Element übergeben werden. Wenn es bereits eine Lösung für dieses Problem gibt, verlinken Sie bitte die Seite.

JRE / JDK-Implementierung:

>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)

Betrachten Sie den folgenden Code:

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();
    }

}

}

UPDATE: Ich habe den Test so geändert, dass er zur Laufzeit alle Befehle akzeptiert, und habe ihn auf meinen Linux-VPS-Server hochgeladen. Ich habe es von einer SSH-Sitzung aus ausgeführt, und alle untergeordneten Prozesse können problemlos gelesen und beschrieben werden. Eines ist mir aufgefallen. Als ich eine interaktive Bash-Shell als untergeordneter Prozess öffnete und dann die Ausgabe in eine Datei umleitete, hat CentOS meines Erachtens mein Programm gestoppt. Das oder mein Programm ist abgestürzt.

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

[1]+  Stopped                 java -jar piotest.jar
[admin@comp8nerd4u2 piotest]$

In der ersten Zeile gebe ich den Befehl ein. Die zweite Zeile ist die Bash-Shell, die erzeugt wurde, aber ich habe nie etwas hineingetippt, damit mein Programm sie nach dem Timeout beendet. Es wird für den zweiten Test vorbereitet, erstellt die Datei "piotest.txt" und stürzt dann entweder ab oder wird vom Betriebssystem gestoppt. Der eigentliche Test selbst war unverändert, mit der Ausnahme, dass Sie jetzt über den Test eingeben können, welcher Befehl zur Laufzeit ausgeführt werden soll. Dies funktioniert gut unter Linux, aber nicht unter Windows. Ich hoffe, dass jemand, der die Win32-API kennt, irgendwie erklären kann, warum dieser Test unter Windows fehlschlägt.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage