173

Is there a way to make bash display stderr messages in red color?

kolypto
  • 11,588

11 Answers11

144

Method 1: Use process substitution directly:

command 2> >(sed $'s,.*,\e[31m&\e[m,'>&2)

Method 2: Create a function in bash or zsh :

color()(set -o pipefail;"$@" 2> >(sed $'s,.*,\e[31m&\e[m,'>&2))
export -f color

Use it like this:

$ color command

Both methods will show the command's stderr in red.

Keep reading for an explanation of how it works. There are some interesting features demonstrated by these commands. The first 3 bullet points only apply to Method 2. The rest apply to both methods.

  • color()... — Creates a bash function called color.
  • set -o pipefail — This is a shell option that preserves the error return code of a command whose output is piped into another command. This is done in a subshell, which is created by the parentheses, so as not to change the pipefail option in the outer shell.
  • "$@" — Executes the arguments to the function as a new command. "$@" is equivalent to "$1" "$2" ...
  • 2> >(...) — The >(...) syntax is called process substitution. Preceded by 2> , it connects the stderr of the main command to the stdin of the sed process inside the parentheses.
  • sed ... — Because of the redirects above, sed's stdin is the stderr of the executed command. Its function is to surround each line with color codes.
  • $'...' A bash construct that causes it to understand backslash-escaped characters
  • .* — Matches the entire line.
  • \e[31m — The ANSI escape sequence that causes the following characters to be red
  • & — The sed replace character that expands to the entire matched string (the entire line in this case).
  • \e[m — The ANSI escape sequence that resets the color.
  • >&2 — Shorthand for 1>&2, this redirects sed's stdout to stderr.
Alex Bozhenko
  • 93
  • 1
  • 4
killdash9
  • 1,556
124
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
34

You can also check out stderred: https://github.com/sickill/stderred

ku1ik
  • 441
24

The bash way of making stderr permanently red is using 'exec' to redirect streams. Add the following to your bashrc:

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "\033[31m${line}\033[0m"
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

I have posted on this previously: How to set font color for STDOUT and STDERR

gospes
  • 349
14

http://sourceforge.net/projects/hilite/

quaie
  • 1,132
7

I've made a wrapper script that implements Balázs Pozsár's answer in pure bash. Save it in your $PATH and prefix commands to colorize their output.

#!/bin/bash

if [ $1 == "--help" ] ; then echo "Executes a command and colorizes all errors occured" echo "Example: basename ${0} wget ..." echo "(c) o_O Tync, ICQ# 1227-700, Enjoy!" exit 0 fi

Temp file to catch all errors

TMP_ERRS=$(mktemp)

Execute command

"$@" 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" | tee --append $TMP_ERRS; done) EXIT_CODE=$?

Display all errors again

if [ -s "$TMP_ERRS" ] ; then echo -e "\n\n\n\e[01;31m === ERRORS === \e[0m" cat $TMP_ERRS fi rm -f $TMP_ERRS

Finish

exit $EXIT_CODE

kolypto
  • 11,588
3

You can use a function like this


 #!/bin/sh
color() {
      printf '\033[%sm%s\033[m\n' "$@"
      # usage color "31;5" "string"
      # 0 default
      # 5 blink, 1 strong, 4 underlined
      # fg: 31 red,  32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white
      # bg: 40 black, 41 red, 44 blue, 45 purple
      }
string="Hello world!"
color '31;1' "$string" >&2

I append >&2 to print to stderr

Ali Mezgani
  • 3,870
1

I have a slightly modified version of O_o Tync's script. I needed to make these mods for OS X Lion and it's not perfect because the script sometimes completes before the wrapped command does. I've added a sleep but I'm sure there's a better way.

#!/bin/bash

   if [ $1 == "--help" ] ; then
       echo "Executes a command and colorizes all errors occured"
       echo "Example: `basename ${0}` wget ..."
       echo "(c) o_O Tync, ICQ# 1227-700, Enjoy!"
       exit 0
       fi

   # Temp file to catch all errors
   TMP_ERRS=`mktemp /tmp/temperr.XXXXXX` || exit 1

   # Execute command
   "$@" 2> >(while read line; do echo -e "$(tput setaf 1)$line\n" | tee -a $TMP_ERRS; done)
   EXIT_CODE=$?

   sleep 1
   # Display all errors again
   if [ -s "$TMP_ERRS" ] ; then
       echo -e "\n\n\n$(tput setaf 1) === ERRORS === "
       cat $TMP_ERRS
   else
       echo "No errors collected in $TMP_ERRS"
   fi
   rm -f $TMP_ERRS

   # Finish
   exit $EXIT_CODE
Cliff
  • 111
1

This solution worked for me: https://superuser.com/questions/28869/immediately-tell-which-output-was-sent-to-stderr

I've put this function in my .bashrc or .zshrc:

# Red STDERR
# rse <command string>
function rse()
{
    # We need to wrap each phrase of the command in quotes to preserve arguments that contain whitespace
    # Execute the command, swap STDOUT and STDERR, colour STDOUT, swap back
    ((eval $(for phrase in "$@"; do echo -n "'$phrase' "; done)) 3>&1 1>&2 2>&3 | sed -e "s/^\(.*\)$/$(echo -en \\033)[31;1m\1$(echo -en \\033)[0m/") 3>&1 1>&2 2>&3
}

Then for example:

$ rse cat non_existing_file.txt

will give me a red output.

Eyal Levin
  • 121
  • 3
1

using xargs and printf:

command 2> >(xargs -0 printf "\e[31m%s\e[m" >&2)
0

a version using fifos

mkfifo errs
stdbuf -o0 -e0 -i0 grep . foo | while read line; do echo -e "\e[01;31m$line  \e[0m" >&2; done &
stdbuf -o0 -e0 -i0 sh $script 2>errs
untore
  • 101