7

Is there a way to capture console output from a process with color formatting data? Currently I am capturing just the text output with:

ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", "mvn dependency:resolve");
// mvn dependency:resolve is an example of a process that outputs color
Process p = builder.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) 
{
   System.out.println(line);
}

But I can't find a way to capture color data for this text. The colored text does not seem to start with any special character or anything.

I am printing this captured text to UI for the user to see log of this process but it is hard to read so I would like to copy the colors from the console.

Here is an example of how it can be done in Go, but can this be done in Java?

This application is going to be run on Windows but it would be great if it could also work on Linux or MacOS by reading the same color data from Shell or Bash.

egerardus
  • 11,316
  • 12
  • 80
  • 123
  • 1
    Is the output which is captured done by a batch file or by an application? How is the text coloring in console done? Is it done with [ANSI escape sequences](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) or by direct modification of properties of the console. Captured can be with text stream buffers only characters and not the graphical representation of the output characters. – Mofi Aug 21 '17 at 05:45
  • I am running the msbuild command to build C# solution: https://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx – František Jeřábek Aug 21 '17 at 06:24
  • Please give more details about the “coloring” given to text in the console and some sample output indicating what color it is. – Bohemian Oct 17 '20 at 05:22
  • @Bohemian The question is not about how to print the color. It is about how to find the existing color data from the output of the ProcessBuilder. In other words, I have an existing process that outputs colored text to bash or cmd but I need my Java app to read the text content and the _text color_. For example [here is how it is done in Go](https://stackoverflow.com/q/54476326/1062992). How would I do the same in Java? – egerardus Oct 17 '20 at 05:32
  • If I need to use something other than the BufferedReader (shown above) or something other than ProcessBuilder to run my cmd/bash script to generate the output that will be OK for me. – egerardus Oct 17 '20 at 05:41

2 Answers2

7

The problem you are facing doesn't relate strictly to Java capability.

What happens is that many programs check whether stdin is a terminal or a pipe and generate output differently, dropping color formatting for example. Sometimes workaround exists directly by use of special option to force the generation of those color formatting 1.

If this is not available in the binary you are using, the apprently only solution is to write your own pseudo-tty making the targeted binary think it talks to a regular tty.

You can try to use the code presented here 2 or you can take a look at JetBrains/pty4j and see if you can tweak it to your needs.

nvidot
  • 1,262
  • 1
  • 7
  • 20
  • Thank you! pty4j worked great for me, no tweaking needed. It outputs the color codes in front of the text for me to work with. It's not my question so I can't accept the answer but here is a 200 bounty for you. I'll also post an answer with more specifics. – egerardus Oct 19 '20 at 19:15
1

As mentioned by @nvidot pty4j is the way to go:

public static void main(String[] args) throws IOException, InterruptedException
{
    try
    {

        String[] cmd = { "cmd", "/C", "mvn clean" };
        String workingDir = "d:\\test";
        PtyProcess pty = PtyProcess.exec(cmd, null, workingDir);
        InputStream is = pty.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        String line = null;
        while ((line = reader.readLine()) != null) 
        {
            System.out.println(line);
        }
        int result = pty.waitFor();
    }
    catch (Exception e)
    {
        e.printStackTrace();
        e.getCause().printStackTrace();
    }
    
}

This gives me the following output:

(Obviously it needs to be cleaned up a bit but the color codes are clearly visible in there and ready for parsing, e.g.: "94m", "92m", "36m", etc)

[0m[0K[[0;34;94mINFO[0m] Scanning for projects...[0K[?25l
[0K[?25h[[0;34;94mINFO[0m][0K[?25l
[[0;34;94mINFO[0m] [0;1m-----------------------------< [0;36mtest:test[0;1m >------------------------------[0m[0K
[[0;34;94mINFO[0m] [0;1mBuilding test 1.0-SNAPSHOT[0m[0K
[[0;34;94mINFO[0m] [0;1m----------[0m[0K[15G[?25h[?25l
[[0;34;94mINFO[0m] [0;1m--------------------------------[ jar ]---------------------------------[0m[0K
[0K[?25h[[0;34;94mINFO[0m][0K[?25l
[[0;34;94mINFO[0m] [0;1m--- [0;32mmaven-clean-plugin:3.1.0:clean[0m [0;1m(default-clean)[0m @ [0;36mtest[0;1m ---[0m[0K
[0K[?25h[[0;34;94mINFO[0m] [0;1m------------------------------------------------------------------------[0m[0K[?25l
[[0;34;94mINFO[0m] [0;32;92mBUILD SUCCESS[0m[0K
[[0;34;94mINFO[0m] [0;1m--------------[0m[0K[16G[?25h[?25l
[[0;34;94mINFO[0m] [0;1m------------------------------------------------------------------------[0m[0K
[[0;34;94mINFO[0m] Total time:  1.924 s[0K
[[0;34;94mINFO[0m] Finished at: 2020-10-19T12:36:46-07:00[0K
[[0;34;94mINFO[0m] [0;1m------------------------------[0m[0K[32G[?25h[?25l
[[0;34;94mINFO[0m] [0;1m------------------------------------------------------------------------[0m[0K
[0K[?25h
egerardus
  • 11,316
  • 12
  • 80
  • 123
  • Is there an existing TextPane or TextArea that can interpret these codes and display them correctly? – chrish Nov 09 '22 at 14:31