命令行终端在进程上执行并从此进程输入交互

Command line terminal executing on process and input interaction from this process

基本上,当我按下 java 应用程序上的按钮时,我有一个运行的进程。 而这个进程向OS的终端执行命令。 但有时此命令需要与用户进行交互。 我想知道是否可以在需要时让流程与用户进行交互?

我的代码:

File marsSimulator = new File("resources/mars_simulator/Mars4_5.jar");
if(marsSimulator.exists() && temp.exists()){
   String res="";
   try {
     Process p = Runtime.getRuntime().exec(new String[]{"java","-jar",marsSimulator.getAbsolutePath(),tempAssembly.getAbsolutePath()});
     p.waitFor();

     InputStream is = p.getInputStream();

     byte b[] = new byte[is.available()];
     is.read(b, 0, b.length); // probably try b.length-1 or -2 to remove "new-line(s)"

     res = new String(b);

   } catch (Exception ex) {
     ex.printStackTrace();
   }
}

此外,我忘了说该应用程序是使用 SWING 制作的,并且该过程的输出显示在 TextArea 上...我应该更改任何内容吗?

请注意,当与用户进行交互时,进程会阻塞。如果没有,进程不会阻塞!

在这种情况下我需要做什么(我不知道该怎么做)?

  1. 进程需要交互时。我需要知道进程何时需要一些交互。
  2. 我需要以交互方式(逐行)获取流程生成的输出。

P.S.: 对于想了解流程线的人,我正在使用火星模拟器 (http://courses.missouristate.edu/KenVollmar/MARS/) 并将 jar 应用程序发送到一个带有 mips 汇编代码的进程中.


下一段代码正在与我的项目一起使用

希望对以后的冒险家有所帮助!

感谢 Nicolas Filotto 对我的帮助。

我的 class ObservableStream:

class ObservableStream extends Observable {
  private final Queue<String> lines = new ConcurrentLinkedQueue<>();

  public void addLine(String line) {
    lines.add(line);
    setChanged();
    notifyObservers();
  }

  public String nextLine() {
    return lines.poll();
  }

  public String getLine(){return lines.peek();}
}

以及代码的另一部分:

Process p = Runtime.getRuntime().exec(new String[]{"java","-jar",marsSimulator.getAbsolutePath(),tempAssembly.getAbsolutePath()});

//This code does the interaction from the process with the GUI ! Implied, input interaction+output interaction from the process
ObservableStream out = new ObservableStream();
// Observer that simply sends to my external process line by line what we put in
// the variable output
PrintWriter writer = new PrintWriter(p.getOutputStream(), true);
out.addObserver(
  (o, arg) -> {
    ObservableStream stream = (ObservableStream) o;
    String line;
    while ((line = stream.nextLine()) != null) {
      writer.println(line);
    }
  }
);

ObservableStream input = new ObservableStream();
input.addObserver(
  (o, arg) -> {
    ObservableStream stream = (ObservableStream) o;
    String line;
    while ((line = stream.nextLine()) != null) {
      outputTextArea.appendText(line+"\n");
    }
  }
);

// The thread that reads the standard output stream of the external process
// and put the lines into my variable input
new Thread(
  () -> {
    try (BufferedReader reader = new BufferedReader(
      new InputStreamReader(p.getInputStream()))
    ) {
       String line;
       while ((line = reader.readLine()) != null) {
         input.addLine(line);
      }
    } catch (IOException e1) {
      e1.printStackTrace();
  }
 }
).start();


new Thread(
  ()->{
    while(p.isAlive()){
      String res = input.getLine();
      if(res!=null && res.equals("Enter integer value:")) {
        boolean integerIsRequested=true;
        Thread t=null;
        while(integerIsRequested){
          if(t==null) {
            t = new Thread(new Runnable() {
              public void run() {
                String test1 = JOptionPane.showInputDialog("Enter Integer value:");
                while(!test1.matches("^\d+$")){
                  test1 = JOptionPane.showInputDialog("Error: Not a valid Integer.\nEnter a correct Integer value:");
                }
                Integer i = Integer.valueOf(test1);

                if (i != null) {
                  out.addLine(test1);
                }
              }
            });
            t.start();

          }
          if(!t.isAlive()){
            integerIsRequested=false;
          }
        }
      }
    }
    outputTextArea.appendText("Program executed\n");
  }
).start();

顺便说一下,这个 post 是独一无二的 Jarrod ;)

我认为您无法从 Java 检查进程的状态。但是,您可以使用一些 Linux 命令来完成。 (当然,如果你使用 Linux)

如果您的 Java 进程可以访问 /proc 目录,那么您可以读取该进程的 status 文件。

例如,进程 ID 为 12280

的进程
/proc/12280/status

这是 status 文件的相关输出

Name:   java
State:  S (sleeping)
Tgid:   12280
Pid:    12280
PPid:   12279
...

第二行给出进程的状态。您需要 运行 一个线程来连续轮询此文件以读取状态。

我希望它能回答您的问题... subProcessStuff "emulates" 该子流程。它可以是任何东西——但这样我们就万事俱备了。它需要 2 个参数传递到控制台。字符串和整数。 Gobbler 得到 Callback 这是一个接口,具有匿名实现 - 并且有参数检查。要回答子进程是否等待,我们只需跟踪所说的内容 - 就像用户是否会使用它一样。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Scanner;

class Test1 {
    public static void main(String[] args) {
        for (String arg : args)
            System.out.println("arg: " + arg);

        for (String arg : args)
            if (arg.equals("-test")) {
                subProcessStuff();
                return;
            }
        mainProcess();

    }

    public static void subProcessStuff() {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
            System.out.println("Enter String");
            String s = br.readLine();
            System.out.println("Enered String: " + s);
            System.out.println("Enter Integer:");
            int i = Integer.parseInt(br.readLine());
            System.out.println("Entered Integer: " + i);
        } catch (IOException e) {
            System.err.println("io error - " + e.getMessage());
        } catch (NumberFormatException nfe) {
            System.err.println("Invalid Format!");
        }
    }

    private static PrintStream out;

    public static void mainProcess() {
        String[] commands = { "ls", "-alt" };
        ProcessBuilder builder = new ProcessBuilder("java", "Test1", "-test");

        // builder.inheritIO(); // I avoid this. It was messing me up.

        try {
            Process proc = builder.start();
            InputStream errStream = proc.getErrorStream();
            InputStream inStream = proc.getInputStream();
            OutputStream outStream = proc.getOutputStream();

            new Thread(new StreamGobbler("err", out, errStream)).start();

            out = new PrintStream(new BufferedOutputStream(outStream));

            Callback cb = new Callback() {
                @Override
                public void onNextLine(String line) {
                    if (line.equals("Enter String")) {
                        out.println("aaaaa");
                        out.flush();
                    }
                    if (line.equals("Enter Integer:")) {
                        out.println("123");
                        out.flush();                    
                    }
                }
            };
            new Thread(new StreamGobbler("in", out, inStream, cb)).start();
            int errorCode = proc.waitFor();
            System.out.println("error code: " + errorCode);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }
}

interface Callback {
    void onNextLine(String line);
}

class StreamGobbler implements Runnable {
    private PrintStream out;
    private Scanner inScanner;
    private String name;
    private Callback cb;

    public StreamGobbler(String name, PrintStream out, InputStream inStream) {
        this.name = name;
        this.out = out;
        inScanner = new Scanner(new BufferedInputStream(inStream));
    }

    public StreamGobbler(String name, PrintStream out, InputStream inStream, Callback cb) {
        this.name = name;
        this.out = out;
        inScanner = new Scanner(new BufferedInputStream(inStream));
        this.cb = cb;
    }

    @Override
    public void run() {
        while (inScanner.hasNextLine()) {
            String line = inScanner.nextLine();
            if (cb != null)
                cb.onNextLine(line);
            System.out.printf("%s: %s%n", name, line);
        }
    }
}

为了实现这样的用例,我个人会使用:

  1. 一个 Observable 对象,用于在外部进程提供新行时通知我的 UI
  2. 一个 Observable 对象,我向其添加由 UI
  3. 提供的新行
  4. #1 的 Observer 将刷新我的 UI
  5. 的数据
  6. #2 的 Observer 会将我的 UI 提供的行发送到我的外部进程
  7. A Thread 将检查我的外部进程是否提供了新行,如果是,它会将这些行提供给 #1

因为我没有你的完整环境,我将向你展示它如何与模拟对象一起工作:

首先是我的伪造外部应用程序,它只对他收到的内容执行 Echo

public class Echo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String line = scanner.nextLine();
            System.out.printf("echo > %s%n", line);
        }
    }
}

如果这个class接收到foo,它会打印到标准输出流echo > foo

然后我的Observableclass

public class ObservableStream extends Observable {
    private final Queue<String> lines = new ConcurrentLinkedQueue<>();

    public void addLine(String line) {
        lines.add(line);
        setChanged();
        notifyObservers();
    }

    public String nextLine() {
        return lines.poll();
    }
}

注意: class ObservableStream(到目前为止已经实现)意味着只有一个 Observer 没有更多根据您的需要就足够了。 Indeed 仅用于将您的 UI 与数据的检索或发布方式分离

最后是主要代码:

Process p = Runtime.getRuntime().exec(
    new String[]{"java", "-cp", "/my/path/to/my/classes", "Echo"}
);
// The Observable object allowing to get the input lines from my external process
ObservableStream input = new ObservableStream();
// A mock observer that simply prints the lines provided by the external process
// but in your case you will update your text area instead
input.addObserver(
    (o, arg) -> {
        ObservableStream stream = (ObservableStream) o;
        String line;
        while ((line = stream.nextLine()) != null) {
            System.out.printf("Line Received from the external process: %s%n", line);
        }
    }
);
// The thread that reads the standard output stream of the external process 
// and put the lines into my variable input
new Thread(
    () -> {
        try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(p.getInputStream()))
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                input.addLine(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
).start();
// The Observable object allowing to send the input lines to my external process
ObservableStream output = new ObservableStream();
// Observer that simply sends to my external process line by line what we put in
// the variable output
PrintWriter writer = new PrintWriter(p.getOutputStream(), true);
output.addObserver(
    (o, arg) -> {
        ObservableStream stream = (ObservableStream) o;
        String line;
        while ((line = stream.nextLine()) != null) {
            writer.println(line);
        }
    }
);

// A simple scanner used to send new messages to my external process
Scanner scanner = new Scanner(System.in);
while (true) {
    output.addLine(scanner.nextLine());
}

如果这段代码接收到foo,它会打印到标准输出流Line Received from the external process: echo > foo

逐行代码我用来与不同的 jar 交互,speechRecognizer.I 认为你想要实现这样的目标。

示例:

我正在交互的 jar(speechRecognizer) 正在执行不同的命令和 运行 一些其他 Threads.Every 时间它必须与主 jar 交互它打印一些我 need.For 示例(用户 said:How 是你),所以你可以有相同的逻辑,当外部 jar 需要与用户交互时,它会打印一些东西,然后你将它读入主 app.So:

// About Process
private Process process;
private BufferedReader bufferedReader;
private boolean stopped = true;
Thread processChecker;

//Running it in a Thread so the app don't lags
new Thread(() -> {
        try {
            stopped = false;
            //Starting the external jar..
            ProcessBuilder builder = new ProcessBuilder("java", "-jar", System.getProperty("user.home")
                    + File.separator + "Desktop" + File.separator + "speechRecognizer.jar", "BITCH_PLEASE");
            //Redirecting the ErrorStream
            builder.redirectErrorStream(true);
            process = builder.start();
            bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;



            //Check continusly if the process is still alive
            //i case of crash i should do something..
            processChecker = new Thread(() -> {
                while (process.isAlive()) {
                    try {
                        Thread.sleep(1200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                stopSpeechReader(false);
            });
            processChecker.start();

            // Continuesly Read Output of external process
            while (!stopped) {
                while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) {
                    System.out.println(line);
                    checkSpeechResult(line);
                }
            }

            // Interrupt the mf Thread if is Alive
            if (processChecker.isAlive())
                processChecker.interrupt();
            System.out.println("SpeechReader Stopped! Process is alive:" + process.isAlive() + " >Exit Value:"
                    + process.exitValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();