简体   繁体   English

如何使用 ProcessBuilder 与 CLI 程序持续交互

[英]How to use ProcessBuilder to continually interact with a CLI program

I use a CLI program regularly which is accessed through a docker container.我经常使用通过 docker 容器访问的 CLI 程序。 Once I enter the container, I can start using my CLI program in question.进入容器后,我就可以开始使用我的 CLI 程序了。 The issue I'm having is I want to continue to interact with the same command line instance.我遇到的问题是我想继续与同一个命令行实例进行交互。 Basically I'm trying to create a GUI program that will run "on top" of a CLI program.基本上我正在尝试创建一个将在 CLI 程序的“顶部”运行的 GUI 程序。 I just don't know how to keep sending commands to the same CLI instance:我只是不知道如何继续向同一个 CLI 实例发送命令:

List<String> command = new ArrayList<String>();
command.add("cmd.exe" );
command.add("/c");
command.add("docker-compose up -d");
System.out.println(command);

ProcessBuilder builder = new ProcessBuilder(command);
builder.inheritIO();
Map<String, String> environ = builder.environment();

Process process = builder.start();
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
    System.out.println(line);
}
command.clear();
command.add("cmd.exe" );
command.add("/c");
command.add("docker ps");
System.out.println(command);

process = builder.start();
is = process.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
while ((line = br.readLine()) != null) {
    System.out.println(line);
}

But this isn't working the way I would like it to.但这不是我想要的方式。 Above, you'll see I'm running two commands: docker-compose up -d and then docker ps .在上面,你会看到我正在运行两个命令: docker-compose up -d然后docker ps But I don't think they are running in the same instance.但我不认为它们在同一个实例中运行。 So if I were to change the directory in the first command, it's not going to remember the directory for the second command.因此,如果我要更改第一个命令中的目录,它不会记住第二个命令的目录。

Also, it seems to be running my commands in reverse order from the order in the code.此外,它似乎以与代码中的顺序相反的顺序运行我的命令。

Instances of class ProcessBuilder are intended to be short-lived, in my opinion.在我看来,class ProcessBuilder的实例是短暂的。 I don't think creating a new instance each time you want to create a new process wastes memory or other resources - but I'm only guessing.我不认为每次要创建新进程时创建新实例都会浪费 memory 或其他资源 - 但我只是猜测。

In any case, to re-use a ProcessBuilder instance in order to execute several processes, you simply use its methods, like command(String...)在任何情况下,要重用ProcessBuilder实例以执行多个进程,您只需使用它的方法,例如command(String...)

I wrote a small Swing app that lets the user enter a [operating system] command and displays that command's output.我编写了一个小型Swing应用程序,它允许用户输入 [操作系统] 命令并显示该命令的 output。 It's not production ready, but I hope it's enough to get you going.它还没有准备好生产,但我希望它足以让你继续前进。

Note that creating and handling a Process in java code is not simple nor intuitive.请注意,在 java 代码中创建和处理Process既不简单也不直观。 The article When Runtime.exec() won't helped me a lot.文章When Runtime.exec() 对我没有多大帮助。 It is an ancient article, but nonetheless still relevant (again, in my opinion).这是一篇古老的文章,但仍然具有相关性(再次,在我看来)。 Simply replace references to class Runtime in the article with class ProcessBuilder since the article was written before ProcessBuilder was added to the JDK.只需将文章中对 class Runtime的引用替换为 class ProcessBuilder ,因为该文章是在将ProcessBuilder添加到 JDK 之前编写的。

Here is the code of my app.这是我的应用程序的代码。 Please refer to the above-mentioned article in order to understand the ProcessBuilder related code.请参考上述文章以了解ProcessBuilder相关代码。 In order to understand the Swing code, I recommend the tutorial Creating a GUI With JFC/Swing为了理解Swing代码,我推荐教程Creating a GUI With JFC/Swing

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class ProcExec implements ActionListener, Runnable {
    private static final String CLEAR = "Clear";
    private static final String EXIT = "Exit";
    private static final String RUN = "Run";

    private JTextArea commandOutput;
    private JTextArea textArea;
    private ProcessBuilder procBuilder;

    public ProcExec() {
        procBuilder = new ProcessBuilder();
    }

    public void actionPerformed(ActionEvent actionEvent) {
        String actionCommand = actionEvent.getActionCommand();
        if (CLEAR.equals(actionCommand)) {
            textArea.setText("");
        }
        else if (EXIT.equals(actionCommand)) {
            System.exit(0);
        }
        else if (RUN.equals(actionCommand)) {
            try {
                execute();
            }
            catch (Exception x) {
                x.printStackTrace();
            }
        }
    }

    public void run() {
        createAndDisplayGui();
    }

    private void createAndDisplayGui() {
        JFrame frame = new JFrame("Process Executor");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(createTopPanel(), BorderLayout.PAGE_START);
        frame.add(createCommandPanel(), BorderLayout.CENTER);
        frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JButton createButton(String text, int mnemonic, String tooltip) {
        JButton button = new JButton(text);
        button.setMnemonic(mnemonic);
        button.setToolTipText(tooltip);
        button.addActionListener(this);
        return button;
    }

    private JPanel createButtonsPanel() {
        JPanel buttonsPanel = new JPanel();
        buttonsPanel.add(createButton(RUN, KeyEvent.VK_R, "Run entered command."));
        buttonsPanel.add(createButton(CLEAR, KeyEvent.VK_C, "Removes entered command."));
        buttonsPanel.add(createButton(EXIT, KeyEvent.VK_X, "Exit application."));
        return buttonsPanel;
    }

    private JSplitPane createCommandPanel() {
        textArea = new JTextArea(30, 40);
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        JScrollPane cmdScrollPane = new JScrollPane(textArea);
        commandOutput = new JTextArea(30, 80);
        JScrollPane outputScrollPane = new JScrollPane(commandOutput);
        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                                              cmdScrollPane,
                                              outputScrollPane);
        return splitPane;
    }

    private JPanel createTopPanel() {
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
        JLabel label = new JLabel("Enter a command...");
        topPanel.add(label);
        return topPanel;
    }

    private int execute() throws IOException, InterruptedException {
        commandOutput.setText("");
        String raw = textArea.getText();
        String[] words = raw.split(" ");
        String[] command = new String[words.length + 2];
        command[0] = "cmd.exe";
        command[1] = "/C";
        System.arraycopy(words, 0, command, 2, words.length);
        procBuilder.command(command);
        Process proc = procBuilder.start();
        ProcHandler stdout = new ProcHandler(proc.getInputStream());
        ProcHandler stderr = new ProcHandler(proc.getErrorStream());
        Thread stdoutThread = new Thread(stdout);
        stdoutThread.start();
        Thread stderrThread = new Thread(stderr);
        stderrThread.start();
        int status = proc.waitFor();
        stderrThread.join();
        stdoutThread.join();
        return status;
    }

    private class ProcHandler implements Runnable {
        private BufferedReader streamReader;

        public ProcHandler(InputStream is) {
            InputStreamReader isr = new InputStreamReader(is);
            streamReader = new BufferedReader(isr);
        }

        public void run() {
            try {
                String line = streamReader.readLine();
                while (line != null) {
                    SwingUtilities.invokeLater(new StreamLine(line));
                    line = streamReader.readLine();
                }
            }
            catch (Exception x) {
                throw new RuntimeException("Stream reading failed.", x);
            }
        }
    }

    private class StreamLine implements Runnable {
        private final String text;

        public StreamLine(String txt) {
            text = txt + "\n";
        }

        public void run() {
            ProcExec.this.commandOutput.append(text);
        }
    }

    public static void main(String[] args) {
        ProcExec instance = new ProcExec();
        EventQueue.invokeLater(instance);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM