Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I want to set my terminal up so stderr is printed in a different color than stdout; maybe red. This would make it easier to tell the two apart.

Is there a way to configure this in .bashrc? If not, is this even possible?


Note: This question was merged with another that asked for stderr, stdout and the user input echo to be output in 3 different colours. Answers may be addressing either question.

share|improve this question
    
Same question on Stack Overflow: stackoverflow.com/questions/6841143/… –  Stéphane Gimenez Dec 18 '11 at 20:04
add comment

6 Answers

up vote 19 down vote accepted

This is a harder version of Show only stderr on screen but write both stdout and stderr to file.

The applications running in the terminal use a single channel to communicate with it; the applications have two output ports, stdout and stderr, but they're both connected to the same channel.

You can connect one of them to a different channel, add color to that channel, and merge the two channels, but this will cause two problems:

  • The merged output may not be exactly in the same order as if there had been no redirection. This is because the added processing on one of the channel takes (a little) time, so the colored channel may be delayed. If any buffering is done, the disorder will be worse.
  • Terminals use color changing escape sequences to determine the display color, e.g. ␛[31m means “switch to red foreground”. This means that if some output destined to stdout arrives just as some output for stderr is being displayed, the output will be miscolored. (Even worse, if there's a channel switch in the middle of an escape sequence, you'll see garbage.)

In principle, it would be possible to write a program that listens on two ptys¹, synchronously (i.e. won't accept input on one channel while it's processing output on the other channel), and immediately outputs to the terminal with appropriate color changing instructions. You'd lose the ability to run programs that interact with the terminal. I don't know of any implementation of this method.

Another possible approach would be to cause the program to output the proper color changing sequences, by hooking around write in a library loaded with LD_PRELOAD. Again, I don't know of any existing implementation.

In practice, if that's applicable, I suggest redirecting stderr to stdout and piping into a pattern-based colorizer such as colortail or multitail, or special-purpose colorizers such as colorgcc or colormake.

¹ pseudo-terminals. Pipes wouldn't work because of buffering: the source could write to the buffer, which would break the synchronicity with the colorizer.

share|improve this answer
    
Thanks, as always. That makes a lot of sense. –  Naftuli Tzvi Kay May 2 '11 at 2:54
    
It might not be difficult to patch a terminal program to colourize the stderr stream. Somebody has suggested something like this at ubuntu brainstorm. –  intuited May 2 '11 at 3:27
    
@intuited: that would require pathcing every terminal emulator you want this to work with. Using LD_PRELOAD trick to intercept write calls seems to be the most appropriate, IMO (but then again, there might be differences on certain *nix flavors.) –  alex May 2 '11 at 13:24
    
At least on Linux, intercepting write alone would not work as most applications do not call directly, but another function from some shared library (like printf) that would call the original write –  Stephane Chazelas Nov 1 '12 at 23:02
1  
The stderred project seems to be an implementation of hooking write via LD_PRELOAD as you describe. –  Drew Noakes Nov 10 '13 at 14:41
show 2 more comments

Here's a proof of concept I did a while back.

It only works in zsh.

# make standard error red
rederr()
{
    while read -r line
    do
        setcolor $errorcolor
        echo "$line"
        setcolor normal
    done
}

errorcolor=red

errfifo=${TMPDIR:-/tmp}/errfifo.$$
mkfifo $errfifo
# to silence the line telling us what job number the background job is
exec 2>/dev/null
rederr <$errfifo&
errpid=$!
disown %+
exec 2>$errfifo

It also assumes you have a function called setcolor.

A simplified version:

setcolor()
{
    case "$1" in
    red)
        tput setaf 1
        ;;
    normal)
        tput sgr0
        ;;
    esac
}
share|improve this answer
    
There's a much simpler way to do this: exec 2> >(rederr). Both versions will have the issues I mention in my answer, of reordering lines and risking mangled output (particularly with long lines). –  Gilles May 2 '11 at 0:38
    
I tried that, and it didn't work. –  Mikel May 2 '11 at 0:46
    
seterr would have to be a standalone script, not a function. –  Gilles May 2 '11 at 7:09
add comment

See Mike Schiraldi's Hilite which does this for one command at a time. My own gush does this for a whole session, but also has a lot of other features/idiosyncracies that you might not want.

share|improve this answer
add comment

Check out stderred. It uses LD_PRELOAD to hook to libc's write() calls, colorizing all stderr output going to a terminal. (In red by default.)

share|improve this answer
4  
Nice, that library is awesome. The real question is: why doesn't my operating system/terminal come with this preinstalled? ;) –  Naftuli Tzvi Kay Dec 13 '11 at 21:51
add comment

Some previous discussion on serverfault.

See also grc and a helpful blog about it.

share|improve this answer
add comment

Colouring user input is difficult because in half the cases, it is output by the terminal driver (with local echo) so in that case, no application running in that terminal may know when the user is going to type text and change the output colour accordingly. Only the pseudo-terminal driver (in the kernel) knows (the terminal emulator (like xterm) sends it some characters upon some keypress and the terminal driver may send back some characters for echo, but xterm can't know whether those are from the local echo or from what the application output to the slave side of the pseudo terminal).

And then, there's the other mode where the terminal driver is told not to echo, but the application this time outputs something. The application (like those using readline like gdb, bash...) may send that on its stdout or stderr which is going to be difficult to differentiate from something that it outputs for other things than echoing back the user input.

Then to differentiate an application's stdout from its stderr, there are several approaches.

Many of them involve redirecting the commands stdout and stderr to pipes and those pipes read be an application to colour it. There are two problems with that:

  • Once stdout is no longer a terminal (like a pipe instead), many application tend to adapt their behavior to start buffering their output which means that output is going to be displayed in big chunks.
  • Even if it's the same process that processes the two pipes, there's no guarantee that the order the text written by the application on stdout and stderr will be preserved, as the reading process can't know (if there's something to be read from both) whether to start reading from the "stdout" pipe or the "stderr" pipe.

Another approach is to modify the application so that it does colour its stdout and stdin. It is often not possible or realistic to do.

Then a trick (for dynamically linked applications) can be to hijack (using $LD_PRELOAD) the outputting functions called by the application to output something and include code in them that sets the foreground colour based on whether they're meant to output something on stderr or stdout. However, that means hijacking every possible function from every possible shared library called by the application that may potentially end up writing something on stdout or stderr (printf, puts, perror...), and even then, that may modify its behavior. In most cases, that would not be realistic either.

Another approach could be to use PTRACE tricks as strace or gdb do to hook ourself every time the write(2) system call is called and set the output colour based on whether the write(2) is on file descriptor 1 or 2.

However, that's quite a big thing to do.

A trick which I've just been playing with is to hijack strace itself (which does the dirty work of hooking itself before every system call) using LD_PRELOAD, to tell it to change the output colour based on whether it has detected a write(2) on fd 1 or 2.

From looking at strace source code, we can see that all it outputs is done via the vfprintf function. All we need to do is to hijack that function.

The LD_PRELOAD wrapper would look like:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>

int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
  static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
  static int c = 0;
  va_list ap_orig;
  va_copy(ap_orig, ap);
  if (!orig_vfprintf) {
    orig_vfprintf = (int (*) (FILE*, const char *, va_list))
      dlsym (RTLD_NEXT, "vfprintf");
  }

  if (strcmp(fmt, "%ld, ") == 0) {
    int fd = va_arg(ap, long);
    switch (fd) {
    case 2:
      write(2, "\e[31m", 5);
      c = 1;
      break;
    case 1:
      write(2, "\e[32m", 5);
      c = 1;
      break;
    }
  } else if (strcmp(fmt, ") ") == 0) {
    if (c) write(2, "\e[m", 3);
    c = 0;
  }
  return orig_vfprintf(outf, fmt, ap_orig);
}

Then, we compile it with:

cc -Wall -fpic -shared -o wrap.so wrap.c -ldl

And use it as:

LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd

You'll notice how if you replace some-cmd with bash, the bash prompt and what you type appears in red (stderr) while with zsh it appears in black (because zsh dups stderr onto a new fd to display its prompt and echo).

It does appear to work surprisingly well even for applications that you'd expect not (like those that do use colours).

The colouring mode is output on strace's stderr which is assumed to be the terminal. If the application redirects its stdout or stderr, our hijacked strace will keep writing the colouring escape sequences on the terminal.

That solution has its limitations:

  • Those inherent to strace: performance issues, you can't run other PTRACE commands like strace or gdb in it, or setuid/setgid issues
  • It's colouring based on the writes on stdout/stderr of each individual process. So for instance, in sh -c 'echo error >&2', error would be green because echo outputs it on its stdout (which sh redirected to sh's stderr, but all strace sees is a write(1, "error\n", 6)). And in sh -c 'seq 1000000 | wc', seq does a lot or writes to its stdout, so the wrapper will end up outputing a lot of (invisible) escape sequences to the terminal.
share|improve this answer
    
Nice. There were suggestions of preexisting wrappers on the duplicate question. I've flagged the question for merging so that your answer can be seen there. –  Gilles Nov 1 '12 at 22:57
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.