Is there a way to make bash display stderr messages in red color?
11 Answers
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
-
11
-
10Great tip! Suggestion: By adding
>&2
right before; done)
, the output intended for stderr actually is written to stderr. That's helpful if you want to capture the normal output of the program.– henkoOct 31, 2012 at 8:12 -
8The following uses
tput
, and is slightly more readable in my opinion:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Feb 14, 2013 at 21:59 -
3I think executing 2 tput processes for each output line is not elegant at all. Maybe if you would store the output of the tput commands in a variable and use those for each echo. But then again, readability is not really better. Oct 7, 2014 at 12:09
-
2This solution does not preserve whitespace but I like it for its brevity.
IFS= read -r line
should help but doesn't. Not sure why. Jul 1, 2016 at 11:05
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 by2>
, it connects thestderr
of the main command to thestdin
of thesed
process inside the parentheses.sed ...
— Because of the redirects above,sed
'sstdin
is thestderr
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&
— Thesed
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 for1>&2
, this redirectssed
'sstdout
tostderr
.
-
5
-
1
-
2ZSH doesn't recognize the shorthand redirection forms. It just needs two more 1's, i.e. :
zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
– RekinAug 2, 2018 at 10:12 -
1The first solution has a bit of a problem in that the pipeline doesn't wait for the output to complete before exiting, so sometimes the colored output will happen after the next prompt, or, if the calling shell exits at that point, it might not happen at all. To demonstrate that consistently, put a
sleep 1;
before the sed. Dec 25, 2019 at 5:24 -
2
You can also check out stderred: https://github.com/sickill/stderred
-
Wow, this utility is great, the only thing that it would need is to have an apt repository that installs it for all users, with one line, not having to do more work to enable it.– sorinApr 3, 2012 at 14:17
-
Seemed to work well when I tested it with a build script in a separate terminal, but I'm hesitant to use it globally (in
.bashrc
). Thanks though! Aug 24, 2012 at 15:18 -
2In OS X El Capitan, the way this works (DYLD_INSERT_LIBRARIES) is "broken" in system binaries because they are protected by SIP. So it might be better to use the bash options given in other answers.– hmijailApr 22, 2016 at 21:41
-
1@hmijail for MacOS please follow github.com/sickill/stderred/issues/60 so we can find a workaround, a partial one already exists but is a little bit buggy.– sorinJul 16, 2018 at 7:57
-
2This is really ingenious. And it's the only answer I've seen that doesn't scramble the order of stdout and stderr (all shell-based solutions do, as far as I can see). Dec 25, 2019 at 5:29
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
-
-
1This is the best answer by far; easy to implement without installation/requiring sudo privilege, and can be generalized to all commands. Sep 26, 2017 at 2:25
-
1Unfortunately this doesn't play well with command chaining (command && nextCommand || errorHandlerCommand). The error output goes after errorHandlerCommand output. Jun 18, 2018 at 23:30
-
1Similarly, if I
source ~/.bashrc
twice with this, my terminal basically locks up.– DolphSep 11, 2018 at 20:11 -
1Not a viable solution for me: (i) I cannot $sudo su in terminal any longer, (ii) it colours red non stderr streams e.g. $read "Press Enter", (iii) it disrupts the order of stderr and stdout mingling up messages out of order.– afora377May 8, 2022 at 1:06
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
-
2
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
-
4Not addressing the problem. You haven't provided a way of separating stderr from stdout, which is what the O.P. is interested in. Aug 27, 2009 at 1:54
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
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.
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
function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') }
Works for bash and zsh. Can't add this as an answer b/c reputation.