2013-08-27 111 views
2

編輯:代碼現在可以工作!以下是我做的:僅限Java終端從第一個命令打印輸出

package me.nrubin29.jterminal; 

import javax.swing.*; 
import javax.swing.filechooser.FileSystemView; 
import javax.swing.text.SimpleAttributeSet; 
import javax.swing.text.StyleConstants; 
import java.awt.*; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.io.*; 
import java.util.ArrayList; 

public class JTerminal extends JFrame { 

    private JTextPane area = new JTextPane(); 
    private JTextField input = new JTextField("Input"); 

    private SimpleAttributeSet inputSAS = new SimpleAttributeSet(), output = new SimpleAttributeSet(), error = new SimpleAttributeSet(); 

    private File workingFolder = FileSystemView.getFileSystemView().getDefaultDirectory(); 

    public JTerminal() throws IOException { 
     super("JTerminal"); 

     getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 

     StyleConstants.setForeground(inputSAS, Color.GREEN); 
     StyleConstants.setBackground(inputSAS, Color.BLACK); 

     StyleConstants.setForeground(output, Color.WHITE); 
     StyleConstants.setBackground(output, Color.BLACK); 

     StyleConstants.setForeground(error, Color.RED); 
     StyleConstants.setBackground(error, Color.BLACK); 

     input.addKeyListener(new KeyListener() { 
      public void keyPressed(KeyEvent e) { 
       if (e.getKeyCode() == KeyEvent.VK_ENTER) { 
        try { 
         String command = input.getText(); 
         if (command.equals("")) return; 

         setTitle("JTerminal (" + command.split(" ")[0] + ")"); 

         input.setText(""); 
         input.setEditable(false); 

         write(inputSAS, command); 

         Process bash = new ProcessBuilder("bash").directory(workingFolder).start(); 

         OutputStreamWriter outputStreamWriter = new OutputStreamWriter(bash.getOutputStream()); 
         outputStreamWriter.write(command); 
         outputStreamWriter.close(); 

         int code = bash.waitFor(); 

         writeStream(bash.getErrorStream(), error); 
         writeStream(bash.getInputStream(), output); 

         input.setEditable(true); 
         setTitle("JTerminal"); 

         if (code == 0 && command.split(" ").length > 1) workingFolder = new File(command.split(" ")[1]); 

        } catch (Exception ex) { error(ex); } 
       } 
      } 

      public void keyTyped(KeyEvent e) {} 
      public void keyReleased(KeyEvent e) {} 
     }); 

     area.setBackground(Color.black); 
     area.setCaretColor(Color.green); 
     area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); 
     area.setEditable(false); 

     JScrollPane pane = new JScrollPane(area); 
     pane.setBorder(BorderFactory.createLineBorder(Color.GREEN)); 
     pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 
     pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 
     pane.setPreferredSize(new Dimension(640, 460)); 

     input.setBackground(Color.black); 
     input.setForeground(Color.green); 
     input.setCaretColor(Color.green); 
     input.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); 
     input.setBorder(BorderFactory.createLineBorder(Color.GREEN)); 

     add(pane); 
     add(input); 

     Dimension DIM = new Dimension(640, 480); 
     setPreferredSize(DIM); 
     setSize(DIM); 
     setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     setLocationRelativeTo(null); 
     setResizable(true); 
     pack(); 
     setVisible(true); 

     input.requestFocus(); 
    } 

    public static void main(String[] args) throws IOException { 
     new JTerminal(); 
    } 

    private void write(SimpleAttributeSet attributeSet, String... lines) { 
     try { 
      if (lines.length == 0) return; 
      for (String line : lines) { 
       area.getStyledDocument().insertString(area.getStyledDocument().getLength(), line + "\n", attributeSet); 
      } 
      area.getStyledDocument().insertString(area.getStyledDocument().getLength(), "\n", attributeSet); 
     } 
     catch (Exception e) { error(e); } 
    } 

    private void error(Exception e) { 
     write(error, "An error has occured: " + e.getLocalizedMessage()); 
     e.printStackTrace(); //TODO: temp. 
    } 

    private void writeStream(InputStream s, SimpleAttributeSet color) { 
     try { 
      BufferedReader reader = new BufferedReader(new InputStreamReader(s)); 

      ArrayList<String> strs = new ArrayList<String>(); 

      while(reader.ready()) strs.add(reader.readLine()); 

      if (strs.size() > 0) write(color, strs.toArray(new String[strs.size()])); 
     } 
     catch (Exception e) { error(e); } 
    } 
} 

我一直在一個Java終端應用。它的工作原理除了它只打印第一個命令的輸出。當我嘗試不止一次運行ls時,這是一張圖片。

JTerminal GUI

package me.nrubin29.jterminal; 

import javax.swing.*; 
import javax.swing.text.SimpleAttributeSet; 
import javax.swing.text.StyleConstants; 
import java.awt.*; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.io.*; 
import java.util.ArrayList; 

public class JTerminal extends JFrame { 

    private JTextPane area = new JTextPane(); 
    private JTextField input = new JTextField("Input"); 

    private SimpleAttributeSet inputSAS = new SimpleAttributeSet(), output = new SimpleAttributeSet(), error = new SimpleAttributeSet(); 

    public JTerminal() throws IOException { 
     super("JTerminal"); 

     getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 

     StyleConstants.setForeground(inputSAS, Color.GREEN); 
     StyleConstants.setBackground(inputSAS, Color.BLACK); 

     StyleConstants.setForeground(output, Color.WHITE); 
     StyleConstants.setBackground(output, Color.BLACK); 

     StyleConstants.setForeground(error, Color.RED); 
     StyleConstants.setBackground(error, Color.BLACK); 

     final Process bash = new ProcessBuilder("/bin/bash").start(); 

     input.addKeyListener(new KeyListener() { 
      public void keyPressed(KeyEvent e) { 
       if (e.getKeyCode() == KeyEvent.VK_ENTER) { 
        try { 
         String command = input.getText(); 
         if (command.equals("")) return; 

         setTitle("JTerminal (" + command.split(" ")[0] + ")"); 

         input.setText(""); 
         input.setEditable(false); 

         write(inputSAS, command); 

         OutputStreamWriter outputStreamWriter = new OutputStreamWriter(bash.getOutputStream()); 
         outputStreamWriter.write(command); 
         outputStreamWriter.close(); 

         bash.waitFor(); 

         writeStream(bash.getErrorStream(), error); 
         writeStream(bash.getInputStream(), output); 

         input.setEditable(true); 
         setTitle("JTerminal"); 

        } catch (Exception ex) { error(ex); } 
       } 
      } 

      public void keyTyped(KeyEvent e) {} 
      public void keyReleased(KeyEvent e) {} 
     }); 

     area.setBackground(Color.black); 
     area.setCaretColor(Color.green); 
     area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); 
     area.setEditable(false); 

     JScrollPane pane = new JScrollPane(area); 
     pane.setBorder(BorderFactory.createLineBorder(Color.GREEN)); 
     pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 
     pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 
     pane.setPreferredSize(new Dimension(640, 460)); 

     input.setBackground(Color.black); 
     input.setForeground(Color.green); 
     input.setCaretColor(Color.green); 
     input.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); 
     input.setBorder(BorderFactory.createLineBorder(Color.GREEN)); 

     add(pane); 
     add(input); 

     Dimension DIM = new Dimension(640, 480); 
     setPreferredSize(DIM); 
     setSize(DIM); 
     setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     setLocationRelativeTo(null); 
     setResizable(true); 
     pack(); 
     setVisible(true); 

     input.requestFocus(); 
    } 

    public static void main(String[] args) throws IOException { 
     new JTerminal(); 
    } 

    private void write(SimpleAttributeSet attributeSet, String... lines) { 
     try { 
      area.getStyledDocument().insertString(area.getStyledDocument().getLength(), "\n", attributeSet); 
      for (String line : lines) { 
       area.getStyledDocument().insertString(area.getStyledDocument().getLength(), line + "\n", attributeSet); 
      } 
     } 
     catch (Exception e) { error(e); } 
    } 

    private void error(Exception e) { 
     write(error, "An error has occured: " + e.getLocalizedMessage()); 
     e.printStackTrace(); //TODO: temp. 
    } 

    private void writeStream(InputStream s, SimpleAttributeSet color) { 
     try { 
      BufferedReader reader = new BufferedReader(new InputStreamReader(s)); 

      ArrayList<String> strs = new ArrayList<String>(); 

      while(reader.ready()) strs.add(reader.readLine()); 

      if (strs.size() > 0) write(color, strs.toArray(new String[strs.size()])); 
     } 
     catch (Exception e) { error(e); } 
    } 
} 
+0

想測試這個,但顯然它不適用於Mac。您是否嘗試過調試以查看數據丟失的位置? – GGrec

+0

我正在使用Mac。 – nrubin29

+0

噢,那麼我可能會丟失JAR。繼續並調試它。 – GGrec

回答

1

過程對象可以只使用一次,所以後續調用Process.waitFor()只是立即返回(當處理已經終止)。 相反,您必須每次從ProcessBuilder請求一個新的Process。

下面是正確的代碼:

package me.nrubin29.jterminal; 
import javax.swing.*; 
import javax.swing.text.SimpleAttributeSet; 
import javax.swing.text.StyleConstants; 
import java.awt.*; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.io.*; 
import java.util.ArrayList; 

public class JTerminal extends JFrame { 

    private JTextPane area = new JTextPane(); 
    private JTextField input = new JTextField("Input"); 

    private SimpleAttributeSet inputSAS = new SimpleAttributeSet(), output = new SimpleAttributeSet(), error = new SimpleAttributeSet(); 

    public JTerminal() throws IOException { 
     super("JTerminal"); 

     getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 

     StyleConstants.setForeground(inputSAS, Color.GREEN); 
     StyleConstants.setBackground(inputSAS, Color.BLACK); 

     StyleConstants.setForeground(output, Color.WHITE); 
     StyleConstants.setBackground(output, Color.BLACK); 

     StyleConstants.setForeground(error, Color.RED); 
     StyleConstants.setBackground(error, Color.BLACK); 

     final ProcessBuilder builder = new ProcessBuilder("/bin/bash"); 

     input.addKeyListener(new KeyListener() { 
      public void keyPressed(KeyEvent e) { 
       if (e.getKeyCode() == KeyEvent.VK_ENTER) { 
        try { 
         String command = input.getText(); 
         if (command.equals("")) return; 

         setTitle("JTerminal (" + command.split(" ")[0] + ")"); 

         input.setText(""); 
         input.setEditable(false); 

         write(inputSAS, command); 

           Process bash = builder.start(); 
         OutputStreamWriter outputStreamWriter = new OutputStreamWriter(bash.getOutputStream()); 
         outputStreamWriter.write(command); 
         outputStreamWriter.close(); 

         bash.waitFor(); 

         writeStream(bash.getErrorStream(), error); 
         writeStream(bash.getInputStream(), output); 

         input.setEditable(true); 
         setTitle("JTerminal"); 

        } catch (Exception ex) { error(ex); } 
       } 
      } 

      public void keyTyped(KeyEvent e) {} 
      public void keyReleased(KeyEvent e) {} 
     }); 

     area.setBackground(Color.black); 
     area.setCaretColor(Color.green); 
     area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); 
     area.setEditable(false); 

     JScrollPane pane = new JScrollPane(area); 
     pane.setBorder(BorderFactory.createLineBorder(Color.GREEN)); 
     pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 
     pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 
     pane.setPreferredSize(new Dimension(640, 460)); 

     input.setBackground(Color.black); 
     input.setForeground(Color.green); 
     input.setCaretColor(Color.green); 
     input.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); 
     input.setBorder(BorderFactory.createLineBorder(Color.GREEN)); 

     add(pane); 
     add(input); 

     Dimension DIM = new Dimension(640, 480); 
     setPreferredSize(DIM); 
     setSize(DIM); 
     setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     setLocationRelativeTo(null); 
     setResizable(true); 
     pack(); 
     setVisible(true); 

     input.requestFocus(); 
    } 

    public static void main(String[] args) throws IOException { 
     new JTerminal(); 
    } 

    private void write(SimpleAttributeSet attributeSet, String... lines) { 
     try { 
      area.getStyledDocument().insertString(area.getStyledDocument().getLength(), "\n", attributeSet); 
      for (String line : lines) { 
       area.getStyledDocument().insertString(area.getStyledDocument().getLength(), line + "\n", attributeSet); 
      } 
     } 
     catch (Exception e) { error(e); } 
    } 

    private void error(Exception e) { 
     write(error, "An error has occured: " + e.getLocalizedMessage()); 
     e.printStackTrace(); //TODO: temp. 
    } 

    private void writeStream(InputStream s, SimpleAttributeSet color) { 
     try { 
      BufferedReader reader = new BufferedReader(new InputStreamReader(s)); 

      ArrayList<String> strs = new ArrayList<String>(); 

      while(reader.ready()) strs.add(reader.readLine()); 

      if (strs.size() > 0) write(color, strs.toArray(new String[strs.size()])); 
     } 
     catch (Exception e) { error(e); } 
    } 
} 
+0

好吧,我只是將工作文件夾保存在File實例中。 – nrubin29

+0

或者您可以在每個命令之後設置ProcessBuilder的工作目錄:http://docs.oracle.com/javase/6/docs/api/java/lang/ProcessBuilder.html#directory(java.io.File) – Menthos

+0

On the正確的軌道,但我會關心'bash.waitFor',一旦它返回,它將表明該進程已退出。另外,'waitingFor'應該在讀取過程輸入之後完成...... – MadProgrammer

1

一旦一個進程已經退出,也不能爲「read」或從「寫」來。

您的代碼也會阻止「waitFor」方法,這意味着您的用戶界面將不會更新,直到該進程完成運行。這真的很有用...

相反,您需要啓動該進程並允許它繼續運行,並在後臺監視狀態。

因爲Swing是單線程環境,所以您需要小心處理長時間運行/阻塞過程以及更新UI的方式。

爲此,我使用SwingWorker在後臺線程中讀取Process的輸出,並將更新重新同步回事件分派線程。這允許UI繼續運行並保持響應,同時讀取正在運行的進程的輸出...

另外,不是每次「運行」一個新命令,而是創建一個新進程,您都需要寫入輸入到Process的輸入流。

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.io.File; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.util.List; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JTextArea; 
import javax.swing.JTextField; 
import javax.swing.SwingWorker; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class TestTerminal { 

    public static void main(String[] args) { 
     new TestTerminal(); 
    } 

    public TestTerminal() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException ex) { 
       } catch (InstantiationException ex) { 
       } catch (IllegalAccessException ex) { 
       } catch (UnsupportedLookAndFeelException ex) { 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new TestPane()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class TestPane extends JPanel { 

     private JTextArea output; 
     private JTextField input; 

     private Process process; 

     public TestPane() { 
      setLayout(new BorderLayout()); 

      output = new JTextArea(20, 20); 
      input = new JTextField(10); 

      output.setLineWrap(false); 
      output.setWrapStyleWord(false); 
      output.setEditable(false); 
      output.setFocusable(false); 

      add(new JScrollPane(output)); 
      add(input, BorderLayout.SOUTH); 

      input.addActionListener(new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        String cmd = input.getText() + "\n"; 
        input.setText(null); 
        output.append("\n" + cmd + "\n\n"); 
        if (process == null) { 
         ProcessBuilder pb = new ProcessBuilder("bash"); 
         pb.directory(new File(".")); 
         try { 
          process = pb.start(); 
          InputStreamWorker isw = new InputStreamWorker(output, process.getInputStream()); 
          isw.execute(); 
         } catch (IOException ex) { 
          ex.printStackTrace(); 
          input.setEnabled(false); 
         } 

         new Thread(new Runnable() { 
          @Override 
          public void run() { 
           int exit = -1; 
           try { 
            exit = process.waitFor(); 
           } catch (InterruptedException ex) { 
           } 
           System.out.println("Exited with " + exit); 
           input.setEnabled(false); 
          } 
         }).start(); 

        } 
        OutputStream os = process.getOutputStream(); 
        try { 
         os.write(cmd.getBytes()); 
         os.flush(); 
        } catch (IOException ex) { 
         ex.printStackTrace(); 
         input.setEnabled(false); 
        } 
       } 
      }); 
     } 

    } 

    public class InputStreamWorker extends SwingWorker<Void, Character> { 

     private InputStream is; 
     private JTextArea output; 

     public InputStreamWorker(JTextArea output, InputStream is) { 
      this.is = is; 
      this.output = output; 
     } 

     @Override 
     protected void process(List<Character> chunks) { 
      StringBuilder sb = new StringBuilder(chunks.size()); 
      for (Character c : chunks) { 
       sb.append(c); 
      } 
      output.append(sb.toString()); 
     } 

     @Override 
     protected Void doInBackground() throws Exception { 
      int in = -1; 
      while ((in = is.read()) != -1) { 
       publish((char)in); 
      } 
      return null; 
     } 

    } 

} 

我也建議你避免KeyListener你可以在哪裏。 JTextField利用了一個ActionListener,當用戶按下「動作」鍵時會被調用,對於給定的平臺什麼可能是...