Sto provando a creare una sorta di console/terminale che consente all'utente di immettere una stringa, che viene quindi trasformata in un processo e i risultati vengono stampati. Proprio come una normale console. Ma ho problemi a gestire i flussi di input/output. Ho esaminato this thread, ma quella soluzione purtroppo non si applica al mio problema.Processo Java con flussi di input/output simultanei
Insieme ai comandi standard come "ipconfig" e "cmd.exe", devo essere in grado di eseguire uno script e utilizzare lo stesso inputstream per passare alcuni argomenti, se lo script richiede input.
Ad esempio, dopo aver eseguito uno script "python pyScript.py", dovrei essere in grado di passare ulteriori input allo script se lo richiede (esempio: raw_input), mentre si stampa anche l'output dallo script. Il comportamento di base che ti aspetteresti da un terminale.
Quello che ho finora:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class Console extends JFrame{
JTextPane inPane, outPane;
InputStream inStream, inErrStream;
OutputStream outStream;
public Console(){
super("Console");
setPreferredSize(new Dimension(500, 600));
setLocationByPlatform(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// GUI
outPane = new JTextPane();
outPane.setEditable(false);
outPane.setBackground(new Color(20, 20, 20));
outPane.setForeground(Color.white);
inPane = new JTextPane();
inPane.setBackground(new Color(40, 40, 40));
inPane.setForeground(Color.white);
inPane.setCaretColor(Color.white);
JPanel panel = new JPanel(new BorderLayout());
panel.add(outPane, BorderLayout.CENTER);
panel.add(inPane, BorderLayout.SOUTH);
JScrollPane scrollPanel = new JScrollPane(panel);
getContentPane().add(scrollPanel);
// LISTENER
inPane.addKeyListener(new KeyListener(){
@Override
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_ENTER){
e.consume();
read(inPane.getText());
}
}
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
});
pack();
setVisible(true);
}
private void read(String command){
println(command);
// Write to Process
if (outStream != null) {
System.out.println("Outstream again");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream));
try {
writer.write(command);
//writer.flush();
//writer.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
// Execute Command
try {
exec(command);
} catch (IOException e) {}
inPane.setText("");
}
private void exec(String command) throws IOException{
Process pro = Runtime.getRuntime().exec(command, null);
inStream = pro.getInputStream();
inErrStream = pro.getErrorStream();
outStream = pro.getOutputStream();
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
String line = null;
while(true){
BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
while ((line = in.readLine()) != null) {
println(line);
}
BufferedReader inErr = new BufferedReader(new InputStreamReader(inErrStream));
while ((line = inErr.readLine()) != null) {
println(line);
}
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
t1.start();
}
public void println(String line) {
Document doc = outPane.getDocument();
try {
doc.insertString(doc.getLength(), line + "\n", null);
} catch (BadLocationException e) {}
}
public static void main(String[] args){
new Console();
}
}
io non utilizzare il citato ProcessBuilder
, dal momento che mi piace di distinguere tra errore e flusso normale.
UPDATE 29.08.2016
Con l'aiuto di @ArcticLord che abbiamo realizzato quello che è stato chiesto alla domanda iniziale. Ora è solo una questione di stirare qualsiasi comportamento strano come il processo di non terminazione. La console ha un pulsante "stop" che chiama semplicemente pro.destroy(). Ma per qualche ragione questo non funziona per processi infinitamente in esecuzione, cioè output di spamming.
Console: http://pastebin.com/vyxfPEXC
InputStreamLineBuffer: http://pastebin.com/TzFamwZ1
codiceEsempio che fa non fermata:
public class Infinity{
public static void main(String[] args){
while(true){
System.out.println(".");
}
}
}
codice di esempio che non ferma:
import java.util.concurrent.TimeUnit;
public class InfinitySlow{
public static void main(String[] args){
while(true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(".");
}
}
}
Grazie mille! In realtà ho trovato una soluzione simile ieri. Ma c'è un piccolo problema con l'errore e il flusso in uscita. Poiché sono in due thread separati, spesso confondono il loro output. Ad esempio: "cmd", "dir", "foo", "bar". dato che foo in un input non valido, cmd restituisce 2 righe (comando e una nuova riga) con outstream e 2 righe (descrizione dell'errore) con errortream. Ma il risultato è spesso: out, err, out, err. – Haeri
Esatto, ma non è possibile scoprire se la stampa dell'output del comando 'dir' è terminata quando l'errortream inizia a stampare che' foo' non è un comando. Quindi penso che tu possa usare solo 'ProcessBuilder' con 'redirectErrorStream' e ignorare il secondo thread qui. Questo risolverebbe questo problema. – ArcticLord
Stavo sperimentando con redirectErrorStream, ma non sembrava che stampasse effettivamente il flusso degli errori. Solo outStream. E anche non appena avrebbe stampato un errorStream, ha appena terminato il processo. (PS: Mi piace anche avere flussi separati, perché posso contrassegnare l'errortream con il colore rosso). Se solo ci fosse un modo per portarli nel giusto ordine ... – Haeri