编辑:代码现在有效!我是这样做的:
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
时的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 :(得分:1)
Process对象只能使用一次,因此后续对Process.waitFor()的调用只会立即返回(因为进程已经终止)。 相反,您必须每次从ProcessBuilder请求一个新进程。
这是正确的代码:
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); }
}
}
答案 1 :(得分:1)
一旦进程退出,就无法“读取”或“写入”。
您的代码也将阻止“waitFor”方法,这意味着在流程完成运行之前,您的UI不会更新。这真的很有用......
相反,您需要启动该流程并允许其继续运行,并在后台监控状态。
由于Swing是一个单线程环境,因此您需要注意如何处理长时间运行/阻塞进程和UI更新。
为此,我使用SwingWorker
来读取后台线程中Process
的输出,并将更新重新同步回Event Dispatching Thread。这允许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
,当用户按下“操作”键时将调用该{{1}},这对于给定的平台可能是什么......