2012-05-20 42 views
9

在Windows下,當我的程序從命令行啓動時,無法可靠地操作子進程的I/O。這很令人沮喪,因爲它是服務器使用控制檯進行I/O的標準。圖形用戶界面很好,但我更喜歡堅持命令行並保持簡單。我注意到當我從Eclipse IDE執行我的服務器時,子進程I/O就沒有問題,但是從命令行運行完全不同的故事。我無法讀取或寫入子進程,但該進程仍將運行。我在下面寫了一些測試代碼來證明這個問題,我希望這個問題可以在另一臺機器上覆制,然後希望能夠從中解決問題。從Eclipse執行時,繼承的I/O按預期工作。但是,從Windows命令提示符處執行時,無法讀取或寫入子進程。在這兩種情況下,將子進程輸出重定向到文件總是成功,但輸入仍然不能傳遞給子進程。如果已經有解決這個問題的方法,請鏈接頁面。當設置爲父級的標準IO(命令提示符)時,Windows Java子進程不會輸入或輸出

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會話中運行它,並且所有子進程的I/O都可以輕鬆讀取和寫入。有一件事我注意到了。當我作爲一個子進程打開一個交互式bash shell,然後將其輸出重定向到一個文件時,我想,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 shell,但我從來沒有輸入任何東西,所以我的程序在超時後殺死它。它準備好進行第二次測試,創建「piotest.txt」文件,然後崩潰或被OS停止。實際的測試本身並沒有改變,只是測試現在允許你輸入在運行時運行的命令。這在linux中正常工作,但不在Windows中。我希望知道Win32 API的人能夠以某種方式解釋爲什麼這個測試在Windows中失敗。

+0

沒有什麼內在的原因,這不應該工作在Windows上,它看起來就像Java 7運行時中的一個bug一樣。 –

+0

你還在爲此工作嗎?我可能會有一個建議。 –

+0

解決方法是手動流式I/O。我認爲JVM實現只是針對繼承的I/O而被竊聽。我想里程會因不同的實施而有所不同。 – comp8nerd4u2

回答

2

你見過這篇文章嗎? http://www.javaworld.com/jw-12-2000/jw-1229-traps.html?page=1

這聽起來像你需要在Windows上服務輸入/輸出流。本文是關於Runtime.exec的,但我敢打賭,ProcessBuilder的本機代碼非常相似,並且在Windows上具有相同類型的問題。

我爲什麼在Windows上的Eclipse上工作的猜測是Eclipse代表您正在爲這些流服務,以便在控制檯視圖中顯示內容。

+0

OP明確要求現有的標準輸入和輸出由子進程繼承,這意味着沒有需要服務的I/O流。 (該功能僅在Java 7中添加,在您引用的文章編寫時並不存在)。 –

1

我知道我遲到了,但在遇到這個問題之前,我遇到了這個問題,並且想要救救其他人在同一條船上進行搜索。

這實際上是一個Windows的已知的bug: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(); 
相關問題