0

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.

Higher-Kinded Type
  • 1,964
  • 5
  • 27
  • 44
  • 1
    With pure bash, I can't think of any other way than doing the substitutions one by one. What doesn't work with sed? `sed -E "s/(app=|line=)/$color_red\1$color_end/g" <<< "$output"` – Fravadona Feb 16 '22 at 01:06
  • 2
    Does [this answer](https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux/69885917#69885917) cover what you're trying to do? BTW, parameter substitutions with `${var/pattern/replacement}` use glob patterns, not regular expressions, and don't support backreferences. – Gordon Davisson Feb 16 '22 at 01:11
  • @Fravadona I did not include the `-E` flag and was getting bad substitution error. But with that, I think I am in a better shape if not absolutely done with what I am doing. Let me pursue with `-E` and see how it goes. Thanks a ton. If you could post it as a reply, I would mark it as answer because it definitely according to me is a working solution. – Higher-Kinded Type Feb 16 '22 at 01:19
  • @GordonDavisson Approach wise I should say in my mind it was along the lines of the post you have linked. I did not want to perform substitutions as separate commands (or pipe it). The sed with multiple `-e`s is a life-saver. Thanks for sharing the link. If you could post it as a reply, I would mark it as answer because it definitely according to me is a working solution. – Higher-Kinded Type Feb 16 '22 at 01:21

1 Answers1

1

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'.
dan
  • 4,846
  • 6
  • 15