206

There is a simlar question in Preserve ls colouring after grep’ing but it annoys me that if you pipe colored grep output into another grep that the coloring is not preserved.

As an example grep --color WORD * | grep -v AVOID does not keep the color of the first output. But for me ls | grep FILE do keep the color, why the difference ?

2
  • 2
    This question appears to be off-topic because it is about Unix command usage, belongs to unix.stackexchange.com Commented Jun 4, 2014 at 7:02
  • 1
    One way I've been dealing with this is to circumvent/work around the problem whenever possible. That is, avoiding piping to grep is sometimes doable. We can use grep fobar $(find . -name \*.json) instead of find . -name \*.json | xargs grep fobar for example. Commented Jul 21, 2023 at 18:16

4 Answers 4

220

grep sometimes disables the color output, for example when writing to a pipe. You can override this behavior with grep --color=always

The correct command line would be

grep --color=always WORD * | grep -v AVOID 

This is pretty verbose, alternatively you can just add the line

alias cgrep="grep --color=always" 

to your .bashrc for example and use cgrep as the colored grep. When redefining grep you might run into trouble with scripts which rely on specific output of grep and don't like ascii escape code.

Sign up to request clarification or add additional context in comments.

6 Comments

This solution only works under certain lucky circumstances. See andersonvom's answer below.
In my case (Ubuntu) I had already alias to grep. alias grep='grep --color=auto so I need to just alter my ~/.bashrc where it was defined in the first place.
Not working for me - similar to this scenario stackoverflow.com/a/7640077/248616
This nice creative idea is just simple and working stackoverflow.com/a/36288791/248616
the question is about how to make the second grep not supress the color of the first one. This is not an answer to that.
|
84

A word of advice:

When using grep --color=always, the actual strings being passed on to the next pipe will be changed. This can lead to the following situation:

$ grep --color=always -e '1' * | grep -ve '12' 11 12 13 

Even though the option -ve '12' should exclude the middle line, it will not because there are color characters between 1 and 2.

5 Comments

If you use --color=auto, then it should pick up the fact you're piping it somewhere else and suppress colors altogether, but it kind of defeats the purpose. If you still want colored results, I guess you could grep it again at the last pipe using --color.
On Mac OS X, at least, coloring at the last pipe as @andersonvom suggests does not work. Not sure why; maybe it's coloring the excluded pattern? --color=always does work, with, I assume, the caveats stated above.
You have to have the same grep pattern twice in your expression. I just tested this on a mac: http://i.imgur.com/BhmwAlF.png
The easiest but least informative answer I can give is that you should do all of your exclusions earlier in the pipeline and finish with a positive match, ideally a union of all the patterns you want highlighted so it does so in a single step. As an aside, I believe you will also run into matches with the escape codes/sequences themselves, I don't see why you wouldn't. \e[38;5;155m 12 14 16 18 \e[m should match 15. I'm not good enough with Grep to tell you if it's possible to match "15 but not between \e[ or help with the "12" split by formatting.
Oh, when formatting is added, won't the ith character become the (i+n)th with formatting? (N is the number of escape code characters by the ith original character.) Then you have correspondence, as long as you can identify the escape codes (\e[...m?). You can find the start and end of a match in a raw line, then find the corresponding point of each in the formatted line. E.g. 11 12 13 has "12" from 3-5 which is 3-17 in 11 1\e[38;5;155m2\e[39m 13, or 3-20 depending. Then bold it yourself: 11 \e[1m1\e[38;5;155m2\e[39m\e[2m 13 (which moves the correspondence to 9-23.)
37

The existing answers only address the case when the FIRST command is grep (as asked by the OP, but this problem arises in other situations too).

More general answer

The basic problem is that the command BEFORE | grep, tries to be "smart" by disabling color when it realizes the output is going to a pipe. This is usually what you want so that ANSI escape codes don't interfere with your downstream program.

But if you want colorized output emanating from earlier commands, you need to force color codes to be produced regardless of the output sink. The forcing mechanism is program-specific.

Git: use -c color.status=always

git -c color.status=always status | grep -v .DS_Store 

Note: the -c option must come BEFORE the subcommand status.

Others

(this is a community wiki post so feel free to add yours)

1 Comment

Some Git commands also have their own --color=always option, as in: git branch --color=always | grep --color=never -v foo
13

Simply repeat the same grep command at the end of your pipe.
grep WORD * | grep -v AVOID | grep -v AVOID2 | grep WORD

4 Comments

Wouldn't reversing the commands provide the same result? grep -v AVOID * | grep WORD
@BrydonGibson probably, but it was an example to show that it works for any number of piped commands
Had to append --color=always to the last command for this to work for me. (The first command producing color was git grep.) And I'll add that grep -v'ing my entire codebase would seem rather excessive...
superuser.com/a/537631 - you can exclude and include within the same pattern. I don't really know if grep -P is okay for you. I believe your command would be something like grep -P '(?=^((?!AVOID).|^(?!AVOID2).)*$)WORD'. According to the user, it'll "quit" (move on) early if it meets an excluded pattern. I couldn't get it to match WORD more than once though. I'm not a Perl guy and I'm bad with Grep to begin with.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.