# How to read ProcessBuilder's output stream in controlP5's console in the sketch?

**URL:** <https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909>\
**Category:** Libraries\
**Created:** [May 16, 2022, 6:47pm UTC](https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909 "2022-05-16T18:47:00Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![dattasaurabh82](https://avatars.discourse-cdn.com/v4/letter/d/ba9def/32.png) [@dattasaurabh82](https://discourse.processing.org/u/dattasaurabh82)\
**Post date:** [May 16, 2022, 6:47pm UTC](https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909/1 "2022-05-16T18:47:00Z")

</div>

**Intent:** Run a shell command from sketch on a keyPress event and show the command’s output in Sketch window.

**Idea:** Use _controlp5_ GUI library to draw a console in sketch and use `println()` to print output.

**Sketch** :

```java
import java.io.InputStreamReader;

import controlP5.*;
ControlP5 cp5;

Textarea myTextarea;
Println console;

String cmd[] = {"ping", "-c", "3", "www.bing.com"};

void setup() {
  size(640, 480);
  background(25);

  cp5 = new ControlP5(this);
  cp5.enableShortcuts();

  myTextarea = cp5.addTextarea("txt")
    .setPosition(10, 10)
    .setSize(width - 10*2, height-10*2)
    .setColor(color(80, 90, 90))
    .setColorBackground(color(#1D1F21))
    .setColorForeground(color(#F0C674))
    ;
  console = cp5.addConsole(myTextarea);
}

void draw() {
  background(25);

  if (run) {
    println("Running command ...\n");
    // Print the command to show before running
    StringBuffer cmd_buffer = new StringBuffer();
    for (int i = 0; i < cmd.length; i++) {
      cmd_buffer.append(cmd[i]+" ");
    }
    println(cmd_buffer.toString());

    //--------
    ProcessBuilder pb = new ProcessBuilder(cmd);
    pb.inheritIO();
    try {
      Process process = pb.start();
      InputStream inputStream = process.getInputStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
      String res = "";
      while ((res = reader.readLine()) != null) {
        // This part get's printed in the Procesisng IDE's console, why?
        println(res);
      }
      int exitVal = process.waitFor();
      // While this part get's printed in the Procesisng Sketch's console (as made to run)
      println(exitVal);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    //--------

    run = false;
  }
}

boolean run;
void keyPressed() {
  if (key == 'r') {
    run = true;
  }
}

```

The questions are in then comments.  
But basically some outputs are rendered in the sketch console (the end result, after the command finishes running), while the rest of the result output (while the command is running) is rendered in the Processing’s IDE console.

Not a java ninja clearly, so some pointers here would really help. 😅

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [May 16, 2022, 9:50pm UTC](https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909/2 "2022-05-16T21:50:01Z")

</div>

I was unable to redirect Process Builder output to the controlP5 textArea. My conclusion is that the IDE console is the default output unless there is code to redirect the output. It will send the output there even if println(res) is not used (REMMed out). Unfortunately I could only redirect the output to a file and not a widget. The work around below will send the output to a file in the sketch folder, then load the string lines back into a Processing window. Someone else may know a more correct way to get output redirected to a widget. This was done on a Mac; you will have to change the sketch path to fit your system. You should see a current calendar in the Processing window. It’s possible that a Windows system would use a different command string to get a current calendar.

```auto
import java.io.*;

String[] lines;
PFont font;

void setup() {
  size(400, 400);
  String[] commands = {"/bin/sh", "-c", "cal"};
  try {
    ProcessBuilder pb = new ProcessBuilder(commands);
    pb.redirectErrorStream(true);
    pb.redirectOutput(new File("/Users/yourName/Documents/Processing/ProcessBuilder_example/cal.txt"));
    Process p = pb.start();
    BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String line = null;
    StringBuilder output = new StringBuilder();
    while ((line = reader.readLine()) != null) {
      output.append(line);
    }
    int exitVal = p.waitFor();
    println("exit value = ",exitVal);
  }
  catch(Exception e) {
    e.printStackTrace();
  }
  lines = loadStrings("cal.txt");
}

void draw() {
  fill(0);
  font = createFont("andale mono", 18);
  textFont(font);
  for(int x = 0; x < lines.length; x++){
    text(lines[x], 30, 30 + 25*x);   
  }
}

```

![cal](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/8/4/842d27599eea5935bbb449b4086b33ff5490d9b9.png)

---

<div class="post-metadata">

**Author:** ![dattasaurabh82](https://avatars.discourse-cdn.com/v4/letter/d/ba9def/32.png) [@dattasaurabh82](https://discourse.processing.org/u/dattasaurabh82)\
**Post date:** [May 17, 2022, 1:51pm UTC](https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909/3 "2022-05-17T13:51:53Z")

</div>

@svan I think there can be better ways than hardcoding a “log file’s path” (sort of) as you have shown here. But anyways, it’s one way, if you are looking at grabbing response just once and the response is simple in terms of output structure.

Also I’d be careful of importing all the _io libs_:  
`import java.io.*;`  
As to my experience if say, you are using some other libs or functions from processing environment, it sometimes creates conflicts (especially if you also have **serial** library).

So I better import just the ones that I would need, in this case just the ` InputStreamReader` class as we need to get re-direction outputs.

---

<div class="post-metadata">

**Author:** ![dattasaurabh82](https://avatars.discourse-cdn.com/v4/letter/d/ba9def/32.png) [@dattasaurabh82](https://discourse.processing.org/u/dattasaurabh82)\
**Post date:** [May 17, 2022, 1:59pm UTC](https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909/4 "2022-05-17T13:59:54Z")

</div>

I basically gave up on `ProcessBuilder` and tried using just `Process` .

**Note:**  
There are some solutions that need to be there to be more generalised. Mine is tailored towards using **controlP5’s** `console` class that re-directs `println()` statements to the `TextArea`. (That was my goal from the beginning)

There are some gotchas that bothered me a bit and are still not clear (Although I found a bandaid solution). You can see them in my comments 🤔. If anyone has any ideas, please let me know.

Here’s the sketch I got working so far.

```auto
import java.io.InputStreamReader;

import controlP5.*;
ControlP5 cp5;

Textarea myTextarea;
Println console;

String cmd[] = {"ping", "-c", "2", "www.bing.com"};
//String cmd[] = {"python3", "-V"};

void setup() {
  size(640, 480);
  background(25);

  cp5 = new ControlP5(this);

  myTextarea = cp5.addTextarea("txt")
    .setPosition(10, 10)
    .setSize(width - 10*2, height-10*2)
    .setColor(color(80, 90, 90))
    .setColorBackground(color(#1D1F21))
    .setColorForeground(color(#F0C674))
    ;
  console = cp5.addConsole(myTextarea);
}

// boolean run = false;
// String newOutputLine = null;

void draw() {
  background(25);

  //if (newOutputLine != null) {
  // println(newOutputLine);
  // newOutputLine = null;
  //}
}

void keyPressed() {
  if (key == 'r') {
    thread("run_cmd");
  }
}

void run_cmd() {
  /*
   Don't know why I had to instantiate console here again. But this came out of trail and error.
   Or else if we use println(), in this threaded fucntion, then we can only call the thread once,
   or basicaly I don't know if the thread is called again, but I do not see any text output in the
   sketch console.
   */
  console = cp5.addConsole(myTextarea);

  // 1. Print the command string
  StringBuffer cmd_buffer = new StringBuffer();
  for (int i = 0; i < cmd.length; i++) {
    cmd_buffer.append(cmd[i]+" ");
  }
  println("\nCMD:\t" + cmd_buffer.toString() + "\n");
  //newOutputLine = "\nCMD:\t" + cmd_buffer.toString() + "\n";

  // 2. Run the command
  try {
    Process p = Runtime.getRuntime().exec(cmd);
    // 3. Create a buffer reader to capture input stream. (Note: we are not capturing error stream
    // but it can be done)
    BufferedReader buff = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String stdIn = null;
    // 4. Read a line and if it's not null, print it.  
    while ((stdIn = buff.readLine()) != null) {
      println(stdIn.toString());
      //newOutputLine = stdIn.toString();
    }
    // 5. Check the exit code to be 100% sure, the command ran successfully (exitCode 0) 
    int exitVal = p.waitFor();
    println("EXIT CODE:\t", str(exitVal));
    //newOutputLine = "EXIT CODE:\t" + str(exitVal);
    buff.close();
  }
  catch (Exception e) {
    println("Some exception happened!\n");
    //newOutputLine = "some exception happened!";
    //e.printStackTrace();
  }
}

```

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [May 17, 2022, 3:09pm UTC](https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909/5 "2022-05-17T15:09:21Z")

</div>

Big improvement; looks like using just exec() is the way to go. The readability on my Mac was pretty poor. For whatever it’s worth, this is what I did to improve it. Thanks for posting.

```auto
void setup() {
  size(640, 480);
  background(209);
  cp5 = new ControlP5(this);
  myTextarea = cp5.addTextarea("txt")
    .setPosition(10, 10)
    .setSize(width - 10*2, height-10*2)
    .setFont(createFont("menlo-bold",12))
    .setLineHeight(14)
    .setColor(color(0))
    .setColorBackground(color(209))
    .setColorForeground(color(0))
    ;
  console = cp5.addConsole(myTextarea);
}

void draw() {
}

```

If you use an empty draw(), ie don’t draw the background again you only need one console = cp5.addConsole();

Addendum: If I try this command String cmd[] = {"/bin/sh", “-c”, “cal”};, I get this error:StringIndexOutOfBoundsException: begin 0, end -1, length 0

---

<div class="post-metadata">

**Author:** ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)\
**Post date:** [May 18, 2022, 1:43am UTC](https://discourse.processing.org/t/how-to-read-processbuilders-output-stream-in-controlp5s-console-in-the-sketch/36909/6 "2022-05-18T01:43:12Z")

</div>

Due to the StringIndexOutOfBoundsException with String cmd[] = {"/bin/sh", “-c”, “cal”}; I looked for another solution and found this reference: [https://www.codejava.net/java-se/swing/redirect-standard-output-streams-to-jtextarea](https://www.codejava.net/java-se/swing/redirect-standard-output-streams-to-jtextarea) which uses a JTextArea added directly to the window. This is also possible in Processing and the following is a modification of the published demo posted on that site. A CustomOutputStream class is used to divert the PrintStream to the JTextArea, obviating the StringIndexOutOfBoundsException.

```auto
/*
https://www.codejava.net/java-se/swing/redirect-standard-output-streams-to-jtextarea
*/

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import javax.swing.text.BadLocationException;

javax.swing.JFrame frame;
java.awt.Canvas canvas;

JTextArea txtArea;
PrintStream standardOut;

//String cmd[] = {"ping", "-c", "3", "www.bing.com"};
String cmd[] = {"/bin/sh", "-c", "cal"};

int _wndW = 500;
int _wndH = 320;

class CustomOutputStream extends OutputStream {
  private JTextArea textArea;

  public CustomOutputStream(JTextArea textArea) {
    this.textArea = textArea;
  }

  @Override
    public void write(int b) throws IOException {
    // redirects data to the text area
    txtArea.append(String.valueOf((char) b));
    // scrolls the text area to the end of data
    txtArea.setCaretPosition(txtArea.getDocument().getLength());
  }
}

void runAction() {

  try {
    Process proc = Runtime.getRuntime().exec(cmd);
    BufferedReader out = new BufferedReader(new InputStreamReader(proc.getInputStream()));
    BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
    PrintStream printStream = new PrintStream(new CustomOutputStream(txtArea));
    String s = "";
    while ((s = out.readLine()) != null) {
      // keeps reference of standard output stream
      standardOut = System.out;
      // re-assigns standard output stream and error output stream
      System.setOut(printStream);
      System.setErr(printStream);
      println(s);
    }
    while ((s = err.readLine()) != null) {
      println(s);
    }
    int exitVal = proc.waitFor();
    println("EXIT CODE:\t", str(exitVal));
  }
  catch (Exception e) {
    println("Some exception happened!\n");
  }
}

void clearAction() {
  try {
    txtArea.getDocument().remove(0, txtArea.getDocument().getLength());
  }
  catch (BadLocationException ex) {
    println("Bad location exception\n");
  }
}

void textArea() {
  txtArea = new JTextArea();
  JScrollPane scrlPane = new JScrollPane(txtArea);
  scrlPane.setBounds(10, 10, _wndW - 20, 220);
  frame.add(scrlPane);
  txtArea.setEditable(true);
  txtArea.setToolTipText("JTextArea");
  //txtArea.setFont(new Font("Menlo", Font.BOLD, 16));
  txtArea.setLineWrap(false);
  txtArea.setWrapStyleWord(true);
  txtArea.repaint();
}

void runBtn() {
  JButton btn = new JButton("Run");
  btn.setBounds(50, 240, 60, 24);
  frame.add(btn);
  // ****Action**** //
  btn.addActionListener( new ActionListener() {
    void actionPerformed(ActionEvent actionEvent) {
      runAction();
    }
  }
  );
  btn.repaint();
}

void clearBtn() {
  JButton btn = new JButton("Clear");
  btn.setBounds(150, 240, 70, 24);
  frame.add(btn);
  // ****Action**** //
  btn.addActionListener( new ActionListener() {
    void actionPerformed(ActionEvent actionEvent) {
      clearAction();
    }
  }
  );
  btn.repaint();
}

void buildWnd() {
  textArea();
  runBtn();
  clearBtn();
}

void setup() {
  frame = (javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
  canvas = (processing.awt.PSurfaceAWT.SmoothCanvas) ((processing.awt.PSurfaceAWT)surface).getNative();
  frame.setBounds(500, 300, _wndW, _wndH);
  frame.remove(canvas);

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      buildWnd(); // Builds components on EventDispatchThread
    }
  }
  );
}

```

 ![println](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/d/fd7779231773b23f21d68d67d2092d8f11bba06a.png)
