-1

There are two Linux command line programs (whiptail and dialog) that provide the ability to display a text UI to the user. I would like to call one of these (preferably whiptail) from within my Java application so that the user can select an option from a predefined list. The following SO questions were helpful to me in figuring out how to call a Linux command from within my code:

How to run linux commands in java code?

Want to invoke a linux shell command from Java

These give helpful tips on how to run a typical Linux command (e.g. "ls"), but my case is a little more complicated (I think) due to the text UI that I would like to have display to the user.

To give an idea of what whiptail does and looks like see this.

  • 2
    I understand that this is your first post; welcome to SO! "How do I" questions tend to be overly vague for this forum. Typically, you try something, show some code, and ask why it is or isn't doing what you expect. In that spirit, try simply calling whiptail and see if you can get it to display. Then, you can mess with getting the result back. BTW, JOptionPane performs a similar function within your code. You would have to do a bit of Swing programming to do what you want, but that may end up being easier. – Steve11235 Dec 06 '17 at 21:28

1 Answers1

-1

Start with ProcessBuilder. Each parameter you want to send the command is a separate element in the command list, for example...

import java.io.IOException;
import java.io.InputStream;

public class Test {

    public static void main(String[] args) throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder(
                "whiptail", "--title", "Check list example", " --checklist",
                "Choose user's permissions", "20", "78", "4",
                "NET_OUTBOUND", "Allow connections to other hosts", "ON",
                "NET_INBOUND", "Allow connections from other hosts", "OFF",
                "LOCAL_MOUNT", "Allow mounting of local devices", "OFF",
                "REMOTE_MOUNT", "Allow mounting of remote devices", "OFF");
        pb.redirectInput(Redirect.INHERIT);
        // I tend to use pb.redirectErrorStream(true);
        // which sends the error stream to the input stream, but
        // then you'd need to still consume it to get the result
        Process p = pb.start();
        InputStreamConsumer errorConsumer = new InputStreamConsumer(p.getErrorStream());

        Scanner input = new Scanner(System.in);
        String option = input.nextLine();

        p.getOutputStream().write(option.getBytes());
        p.getOutputStream().flush();

        int exitCode = p.waitFor();
        System.out.println(exitCode);

        errorConsumer.join();

        System.out.println(errorConsumer.getContent());
    }

    public static class InputStreamConsumer extends Thread {

        private InputStream is;
        private StringBuilder content;

        public InputStreamConsumer(InputStream is) {
            this.is = is;
            content = new StringBuilder(128);
        }

        public String getContent() {
            return content.toString();
        }

        @Override
        public void run() {

            try {
                int value = -1;
                while ((value = is.read()) != -1) {
                    content.append((char)value);
                }
            } catch (IOException exp) {
                exp.printStackTrace();
            }

        }

    }
}

This is very basic, it simply executes the command, consumes it's output into a StringBuilder (to be retrieved later), waits till the command exists and displays the basic results.

Since I don't have access to whiptail, I can't test the code, but if the command is available in the default search path of the OS, it should work, otherwise you'll need to supply the path to the command as part of the first element in the command list

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • This can be simplified by removing InputStreamConsumer and using `pb.inheritIO();` instead. – VGR Dec 06 '17 at 21:54
  • @VGR Yeah, I'm old school ... and a control freak :P – MadProgrammer Dec 06 '17 at 21:55
  • On the other hand, it won't do what OP was asking, since it won't run in a **terminal**. – Thomas Dickey Dec 06 '17 at 23:31
  • @ThomasDickey That would depend if the OP is running the code from the console or not - since that information was not provided, I focused purely on attempting to provide an answer to the specific question – MadProgrammer Dec 06 '17 at 23:33
  • I'd curious as to why this would attract a downvote - does it not provide a solution to executing the request command? Can it be done a different way or better way? In what whys could it be improved? – MadProgrammer Dec 07 '17 at 02:45
  • @MadProgrammer So when I run this it it blocks at 'int exitCode = p.waitFor();'. Is this possibly related to the fact that whiptail needs the user (me) to select an option before it can terminate? whiptail doesn't actually display the text UI. I placed some prints before and after that 'p.waitFor()' line. Do you think it's related to this: https://stackoverflow.com/questions/5483830/process-waitfor-never-returns – Kevin Kelly Dec 07 '17 at 16:08
  • @KevinKelly You need to write to the Process’s OutputStream – MadProgrammer Dec 07 '17 at 18:33
  • @KevinKelly You could use a `Scanner` to get user input (after your `pb.start()`) and write it to the `Process`'s `OutputStream`, but you'll need to do this before `pb.waitFor()` – MadProgrammer Dec 07 '17 at 19:30
  • @KevinKelly I've not tested it, but I've added the ability for to get input from the user and send that input to the process – MadProgrammer Dec 07 '17 at 20:50