2012-02-06 46 views
2

因此,我正在編寫一個Java程序,需要能夠在Windows或Linux上運行,並且需要能夠向命令行(如dir或ls)發出請求。然後它需要從這些命令中獲得結果輸出,並且AFTERWARDS會提示另一個命令。這裏是我的代碼:Java中的命令行(和多線程)

import java.io.*; 
import java.util.*; 

public class myShell 
{ 
    public static void main(String[] args) 
    { 
     Runtime runtime = Runtime.getRuntime(); 
     Process process; 
     Scanner keyboard = new Scanner(System.in); 
     String userInput = ""; 
     String osName = System.getProperty("os.name"); 
     BufferedReader brStdOut = null; 
     String stdOut; 
     BufferedReader brErrorStream = null; 
     String errorStream; 

     while(userInput.compareToIgnoreCase("exit") != 0) 
     { 
      System.out.println(); 
      System.out.print("??"); 

      userInput = keyboard.nextLine(); 

      int indexOfPound = userInput.indexOf('#'); 
      if(indexOfPound == 0) 
      { 
       userInput = ""; 
      } 
      else if(indexOfPound > 0) 
      { 
       userInput = userInput.substring(0, indexOfPound); 
      } 
      userInput = userInput.trim(); 

      try 
      { 
       if(osName.contains("Windows")) 
       { 
        process = runtime.exec("cmd /c " + userInput); 
       } 
       else 
       { 
        process = runtime.exec(userInput); 
       } 

       brStdOut = new BufferedReader(new InputStreamReader(
          process.getInputStream())); 
       brErrorStream = new BufferedReader(new InputStreamReader(
           process.getErrorStream())); 

       while((stdOut = brStdOut.readLine()) != null) 
       { 
        System.out.println(stdOut); 
       } 
       while((errorStream = brErrorStream.readLine()) != null) 
       { 
        System.err.println(errorStream); 
       } 

       process.waitFor(); 
      } 
      catch(Exception e) 
      { 
       System.out.println("Error executing: " + userInput); 
       System.out.println(e.getMessage()); 
      } 
      try 
      { 
       brStdOut.close(); 
      } 
      catch(Exception e) 
      { 
      } 
      try 
      { 
       brErrorStream.close(); 
      } 
      catch(Exception e) 
      { 
      } 
     } 
     System.exit(0); 
    } 
} 

我要指出,我目前正在測試Windows 7的機器上,並且線路上的#後任何事情都視爲註釋。

當我運行這個,說使用dir,結果回來就好了,但嘗試與ls運行它(它給出了一條消息,說它不是Windows中的識別命令),錯誤流可能會打印下一個提示(??)(這是可取的),在下一個提示之後,或部分提示之前和部分提示之後。有沒有一種方法可以在提示之前始終如一地打印錯誤消息?

這裏是正在發生的例子:

??dir 
Volume in drive C is Windows7_OS 
Volume Serial Number is SomeNumbers 

Directory of C:\Users\BlankedOut\Documents\Java\Eclipse\Workspace\Prog1 

02/05/2012 03:48 PM <DIR>   . 
02/05/2012 03:48 PM <DIR>   .. 
02/05/2012 03:48 PM    301 .classpath 
02/05/2012 03:48 PM    387 .project 
02/05/2012 03:48 PM <DIR>   .settings 
02/05/2012 08:39 PM <DIR>   bin 
02/05/2012 08:39 PM <DIR>   src 
       2 File(s)   688 bytes 
       5 Dir(s) 861,635,362,816 bytes free 

??ls 

??'ls' is not recognized as an internal or external command, 
operable program or batch file. 
ls 
'ls' is not recognized as an internal or external command, 
operable program or batch file. 

??exit 

當我嘗試運行一個命令,如日期我的其他問題方面。在DOS窗口中,這給出了一個2行響應並等待更多輸入,但在Java中,我只得到響應的第一行(儘管dir似乎獲得了所有行),但在第一次日期之後線,該程序只是掛起...有誰知道爲什麼這可能會發生?

非常感謝!

回答

1

當運行ls,有一個錯誤,並且誤差將在標準輸出和在錯誤流被示出,

   while((stdOut = brStdOut.readLine()) != null) 
       { 
        System.out.println(stdOut); 
       } 
       while((errorStream = brErrorStream.readLine()) != null) 
       { 
        System.err.println(errorStream); 
       } 

由於上面的代碼塊的,誤差總是STD出後印刷,所以你可以切換循環來獲得理想的輸出。

在日期提示時,它要求提供新日期,其中提示將等待用戶輸入。在你的程序沒有提供任何東西的地方,所以它在那裏等着,你看到Java程序卡住了。

編輯:試試這個,因爲這將使用單獨的線程,

import java.lang.*; 
import java.io.*; 

public class StreamGobbler implements Runnable { 
    String name; 
    InputStream is; 
    Thread thread; 
    String output=""; 

    public StreamGobbler (String name, InputStream is) { 
    this.name = name; 
    this.is = is; 
    } 

    public void start() { 
    thread = new Thread (this); 
    thread.start(); 
    } 

    public void run() { 
    try { 
    InputStreamReader isr = new InputStreamReader (is); 
    BufferedReader br = new BufferedReader (isr); 

     while (true) { 
      String s = br.readLine(); 
      if (s == null) break; 
      output += s; 
     } 
     is.close(); 
     } catch (Exception ex) { 
      System.out.println ("Problem reading stream " + name + "... :" + ex); 
     ex.printStackTrace(); 
     } 
    } 
} 

    public class ProgA { 
    public static void main(String[] args) throws Exception { 
    Runtime rt = Runtime.getRuntime(); 
    Process p = rt.exec("execute command prompt"); 
    StreamGobbler s1 = new StreamGobbler ("stdin", p.getInputStream()); 
    StreamGobbler s2 = new StreamGobbler ("stderr", p.getErrorStream()); 
    s1.start(); 
    s2.start(); 
    p.waitFor(); 
    System.out.println(s2.output + s1.output); 
    } 
    } 
+0

這可能是因爲在dir命令中沒有錯誤,我認爲你應該嘗試去調試並看看那裏發生了什麼 – 2012-02-06 03:44:53

+0

我試圖簡單地切換while循環的順序。結果是,當我試圖運行dir時,沒有生成輸出,程序只是掛起。這並沒有解決提示可能仍然在相對於輸出流的任何地方打印的事實。我認爲結合這兩個區塊可能會有所幫助(請參閱下面的文章 - 沒有空間),但這與切換塊的順序有相同的問題... – 2012-02-06 03:50:45

+0

我已經添加了線程的代碼,我認爲這應該工作。 – 2012-02-06 04:19:37

1

要始終得到輸出以正確的順序確保沖洗()輸出和錯誤流。

System.err.flush(); //after writing to error stream and 
System.out.flush(); //after writing to output stream 

關於date命令,它希望用戶輸入所以你可以做以下操作:

process.getOutputStream().close(); // after runtime.exec 
0

您必須使用單獨的線程在運行狀態下從進程讀取輸出,如果它填補了父/子進程之間的緩衝區基本上會掛起。這在Windows XP上是一個巨大的問題 - 緩衝區很小,如果你沒有立即啓動另一個線程,那麼這個過程就像死了一樣。這就是爲什麼當你在你的評論中提到的錯誤/標準輸出讀者交換時你的程序掛起。

我還沒有試過Windows 7,所以它可能會更好。

如果你真的不關心它們是分開的,它還可以方便地使用Java中的選項來合併輸出流和錯誤流。這樣你就不需要啓動兩個線程。