51

I've been using git diff, which produces colored output. However, I now find I need to use ordinary diff for something, and it's producing a lot of output that is hard to read because of the lack of colors. How do I make diff produce a readable, colored output? Ideally while piping it to less, for easy review of large files.

4 Answers 4

38

Most diff implementations cannot output colors, you need another program, such as colordiff for that. Newer GNU diffutils, however, do have a --color option, so if you're on Linux and have a recent version (after v3.4 I think), then you can do this natively with diff. If your diff doesn't support --color, read on.

Colors in the terminal are printed via ANSI escape codes which less does not interpret by default. To get less to correctly show colors, you need the -r, or even better, -R switch:

colordiff -- "$file1" "$file2" | less -R 

From man less:

 -R or --RAW-CONTROL-CHARS Like -r, but only ANSI "color" escape sequences are output in "raw" form. Unlike -r, the screen appearance is maintained correctly in most cases. ANSI "color" escape sequences are sequences of the form: ESC [ ... m where the "..." is zero or more color specification characters For the purpose of keeping track of screen appearance, ANSI color escape sequences are assumed to not move the cursor. You can make less think that characters other than "m" can end ANSI color escape sequences by setting the environment variable LESSAN‐ SIENDCHARS to the list of characters which can end a color escape sequence. And you can make less think that characters other than the standard ones may appear between the ESC and the m by setting the environment variable LESSANSIMIDCHARS to the list of characters which can appear. 

Alternatively, you can use more which will display colors correctly by default.


If you cannot install external programs, you should be able to get the same output using a more manual approach:

diff a b | perl -lpe 'if(/^</){$_ = "\e[1;31m$_\e[0m"} elsif(/^>/){$_ = "\e[1;34m$_\e[0m"}' 
4
  • 2
    And if someone wants to view the percentage of the data displayed, they have to use less -RM +Gg: superuser.com/questions/64972/… Commented Jun 5, 2019 at 15:03
  • Note that as of v3.4, diff can output color by using --color=always Commented Jan 9 at 16:18
  • Nice, thanks @kjsmita6, but I am guessing that is specific to GNU diff, right? Commented Jan 9 at 16:19
  • @terdon GNU diff yes, but my FreeBSD-based diff on the Mac also has a color flag. Commented Jan 9 at 20:29
33

The other answers here might be out of date. As of coreutils 3.5 diff can indeed produce colored output which is turned off by default when the stdout is not a console.

From the man page:

--color[=WHEN]
colorize the output; WHEN can be never, always, or auto (the default)

To force color output when stdout is a pipe diff --color=always -- "$file1" "$file2" | less -R should work.

3
  • 1
    You can also include alias diff='diff --color=always' in a .bashrc or .zshrc file. Commented Sep 19, 2019 at 14:09
  • 2
    Yes. I'm using alias diff='diff --side-by-side --left-column --color=always' Commented Sep 19, 2019 at 14:45
  • I use alias diff='/usr/bin/diff --color=always ' and alias less='/usr/bin/less -r ' but although the diff is initially coloured on the first few pages of less but on long diffs it sometimes flips back to mono. This might be on jumps which clearly would not affect diff, since it's output is only generated once and does not have to jump, but somehow less loses track of the colours. Commented Nov 2, 2019 at 4:13
11

To pipe colored diff to less:

diff $file1 $file2 | colordiff | less -r 

To make it more readable, by limiting it to a single screen:

diff -uw $file1 $file2 | colordiff | less -r 

And, to cause less not to display if there is only one screens worth of content:

diff -uw $file1 $file2 | tee /dev/stderr | colordiff | less -r -F 

The -F causes less to close immediately if there is less than one screens worht of content, the pipe to stderr is because when less closes you lose the output - by piping to stderr, it gets output even if less does not display.

An alternative (and, I think, better) way, is to just use -X to prevent less clearing the screen:

diff -uw $file1 $file2 | colordiff | less -r -X -F 

This works well for me, but might be specific to bash. colordiff is not a built-in, but is easily installed.

2
  • 2
    The only command he needs is less -r Commented May 17, 2013 at 16:46
  • less -r doesn't work if piping from diff <file1> <file2>, it needs to go through colordiff first. Commented Oct 10 at 11:57
2

I'd use riff:

diff "$A" "$B" | riff 

Or just this, which will implicitly invoke diff under the hood:

riff "$A" "$B" 

Riff not only tells you which lines changed, but also what parts of the lines that changed (see screenshot below).

Riff defaults to paging the output the same way that git does, so you don't need to worry about pager integration.

On top of that, Riff integrates with git so you can get this output from git diff and its friends as well.

Get it here: https://github.com/walles/riff/releases/

riff in action

Disclaimer: I wrote riff myself so of course I'm recommending it :).

1
  • 1
    Very cool! Thanks for writing that, that's a useful tool to have indeed. Commented Feb 2, 2023 at 11:57

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.