Под Windows я не могу надежно манипулировать вводом-выводом моего дочернего процесса, когда моя программа запущена из командной строки. Это расстраивает, поскольку для серверов стандартно использовать консоль для ввода-вывода. Графические интерфейсы хороши, но я бы предпочел придерживаться командной строки и не усложнять. Я заметил, что ввод-вывод дочернего процесса работает нормально, когда я запускаю свой сервер из 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, и все операции ввода-вывода дочерних процессов могут быть легко прочитаны и записаны. Была одна вещь, которую я заметил. Когда я открыл интерактивную оболочку bash в качестве дочернего процесса, а затем перенаправил ее вывод в файл, я думаю, что CentOS остановил мою программу. Это или моя программа потерпела крах.
[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]$
Первая строка — это ввод команды. Вторая строка - это созданная оболочка bash, но я ничего в нее не вводил, поэтому моя программа убивает ее по истечении тайм-аута. Он готовится ко второму тесту, создает файл «piotest.txt», а затем либо падает, либо останавливается ОС. Фактически сам тест не изменился, за исключением того, что тест теперь позволяет вам вводить команду для запуска во время выполнения. Это прекрасно работает в Linux, но не в Windows. Я надеюсь, что кто-то, кто знает Win32 API, может как-то объяснить, почему этот тест не работает в Windows.