51

When I run a program (for example grep or ls) without a pager, its output is colored. However when I run it piping its output to less, no colors are shown.

For example, this command outputs colored output:

grep -r something

but this doesn't:

grep -r something | less

Why? How can I see colors through less?

Mohammad Reza Rezwani
  • 10,286
  • 36
  • 92
  • 128

3 Answers3

71

There are two problems here:

  • Commands like ls —which auto-detect the colour support— don't find support from pipes
  • less is set to just display colour codes by default.

Both can be overcome but it's a bit clunky:

ls --color=always | less -R

This isn't ls specific. Many commands that support colour also have an override argument.


A slightly more in-depth answer is that ls is checking whether or not its STDOUT belongs to a real terminal or not. When you pipe things around, the STDOUT is set to the STDIN of the next command.

You can see this at work in the ls source code. It's using the isatty command (a core POSIX interface) to work out what the situation is:

  • Are colours on by default:

        print_with_color = (i == color_always
                            || (i == color_if_tty
                                && isatty (STDOUT_FILENO)));
    
  • Do we try to output in multiple columns:

    if (format == long_format)
      format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
    
    //...
    
    if (isatty (STDOUT_FILENO))
      {
        format = many_per_line;
        set_quoting_style (NULL, shell_escape_quoting_style);
        qmark_funny_chars = true;
      }
    else
      {
        format = one_per_line;
        qmark_funny_chars = false;
      }
    

grep does a very similar thing, unless explicitly overridden, it'll detect colour support, with isatty:

color_option = isatty (STDOUT_FILENO) && should_colorize ();
Oli
  • 293,335
  • 1
    My last upvote (reached the limit) for today to support you for 100k. But me and Rinzwind want cake when that happens. – hytromo Jun 13 '14 at 14:53
  • I downloaded a program with name was ack-grep. when type ack-grep foo , it will search for foo on the all sub directories.So I did ack-grep foo | less, and the colors gone. But seems this program does not support "--color=always", whatever thanks for help – Mohammad Reza Rezwani Jun 13 '14 at 15:02
  • 3
    @alex ack has a --color argument: ack-grep -i select --color | less -R – Oli Jun 13 '14 at 15:07
  • @Oli .........Oh my God........ the only thing that I can say about your knowledge and help and also power of linux – Mohammad Reza Rezwani Jun 13 '14 at 15:53
  • @ales he stole that line from my comment >:-D – Rinzwind Jun 13 '14 at 19:14
  • @Rinzwind I'd like to say "BWAHAHAAA!" but I didn't even notice :( – Oli Jun 13 '14 at 19:30
  • @oli ok but hakermania and me want cake in 1900 rep >:D – Rinzwind Jun 13 '14 at 19:40
  • I still wonder how ls does its output. Not the colors, I'm talking how it displays the files in columns to optimally fill the whole width of the terminal, and then the second you pipe it to something, it's a straight list. That's some ingenious engineering. – Braden Best Nov 19 '15 at 06:11
  • 2
    @B1KMusic I agree. I agree so much that I found how how it works and updated the answer. You can force columns with a -C argument. – Oli Nov 19 '15 at 09:47
  • For me it doesn't work: printf "a\nb\n" > text; pcregrep -M --color=always "a\nb\n" text | less -R highlights only "a", but should both "a" and "b". – vstepaniuk Dec 27 '19 at 17:12
4

If you're interested in colors in less more generally, you might want to look at lesspipe.sh. See, for example, https://github.com/wofr06/lesspipe.

lesspipe.sh is an input filter for the pager less as described in less's man page. The script runs under a ksh-compatible shell (e.g. bash, zsh) and allows you to use less to view files with binary content, compressed files, archives, and files contained in archives.

It will also colorize shell scripts, perl scripts, etc. similarly to a text editor, but without the use of any "preprocessing" program to do the colorizing.

simbabque
  • 727
  • 1
  • 11
  • 20
DaveEmme
  • 261
0

@oli's answer relies on you being able to provide appropriate flags to the command that produces the colors. That can be difficult if commands are buried in scripts and the like, and it requires working out how to provide appropriate flags separately for various commands that know how to produce ANSI escapes when the terminal environment says they can.

@DaveEmme's answer tells you that you can use lesspipe to colorize output from whatever source, but as far as I know it won't retain the color from commands that already produce it when output goes to the terminal.

If you already have a command that works just fine in your terminal, then it's nice to be able to run that command in such a way that color escapes are kept regardless of the output being piped, so you can feed that into less -R.

unbuffer does this for you. On debian at least you get it with sudo apt install expect-dev.

You can then do:

unbuffer [command] |& less -R

Using |& instead of | might be an unnecessary detail, but it means STDERR gets piped to less as well as STDOUT.

Thanks to https://superuser.com/a/1260695/229226 for this.

mc0e
  • 328
  • 2
  • 11