2012-02-06 51 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中的选项来合并输出流和错误流。这样你就不需要启动两个线程。