Home > Software engineering >  bash: Capture and colorize parts of output before writing to stdout
bash: Capture and colorize parts of output before writing to stdout

Time:02-17

I am capturing output from a command/script in a variable. The output is essentially a block of text. In the context of this question, let us consider this:

output='In java, both Errors and Exceptions are the subclasses of some/long/path/to/file.java class. Error refers to an illegal operation at line=23 and executed by app=client

Problem: I like to colorize parts of the message before writing to stdout. I managed to find a way but makes sense / works just for one substitution.

color_end=$'\33[0m'
color_red=$'\33[31m'

echo "${output//line=/${color_red}LINE=$color_end}"

I like to match and colorize other parts (say <filename>.java, app=) too. I tried using regex in the variable substitution above but none of these work. Getting syntax errors or the like.

echo "${output//(line=|app=)/${color_red}$1$color_end}"

Tried using sed but 1) colors are interpreted so when writing to stdout 2) gets a bit hairy with regex pattern.

What is the best way to achieve this?


FWIW, it is only one color for all the matched parts. Not different colors for different matched parts.

CodePudding user response:

I would do it like this:

# Only set colors if printing to a terminal
test -t 1 && t_red1=$'\033[31m' t_01=$'\033[0m'

# List here the regexes to highlight
echo "$output" |
sed -E 's/[[:alnum:]_] =|[^[:space:]] \.java'/"$t_red1&$t_01/g"
  • This could be done in pure bash using BASH_REMATCH, but sed is simpler, and probably more efficient.
  • The regex for the file path works here, but is brittle - a space in the path will break it.
  • But you can use this template to add the regexes you want to highlight.
  • test -t 1 ensures the variables are only set if stdout (fd 1) is a terminal, which is important.
  • t_red1 refers to terminal control sequence for color red, on file descriptor 1.
  • I use this notation because it's relatively short for embedding in strings, t_ distinguishes from other variables, and the trailing fd number allows for separate control sequences per file descriptor (each of which may or may not be a terminal).
  • For example, to use colored error messages (on fd2/stderr), you would add a line like test -t 2 && t_red2=$'\033[31m' t_02=$'\033[0m'.
  • Related