83

I wanted to format the Unix files conditionally, I am currently working on diff command and wanted to know if it is possible to format the text of the diff command output.

Example:

Matched values should be displayed in green.
Unmatched values should be displayed in red.

Suppose I have two files file1 and file2 and my command is diff file1 file2.

Now I wanted that suppose output contain 5 mismatch then those mismatch should be displayed in Red color. How to achieve this using unix?

In short "Change color to red for the output of diff command for values which mismatch"

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Aman
  • 1,201

15 Answers15

99

diff --color option was added to GNU diffutils 3.4 (2016-08-08)

This is the default diff implementation on most distros, which will soon be getting it.

Ubuntu 18.04 has diffutils 3.6 and therefore has it.

On 3.5 it looks like this:

enter image description here

Tested:

diff --color -u \
  <(seq 6 | sed 's/$/ a/') \
  <(seq 8 | grep -Ev '^(2|3)$' | sed 's/$/ a/')

Apparently added in commit c0fa19fe92da71404f809aafb5f51cfd99b1bee2 (Mar 2015).

Word-level diff

Like diff-highlight. Not possible it seems, feature request: https://lists.gnu.org/archive/html/diffutils-devel/2017-01/msg00001.html

Related threads:

ydiff does it though, see below.

ydiff side-by-side word level diff

https://github.com/ymattw/ydiff

Is this Nirvana?

python3 -m pip install --user ydiff
diff -u a b | ydiff -s

Outcome:

enter image description here

If the lines are too narrow (default 80 columns), fit to screen with:

diff -u a b | ydiff -w 0 -s

Contents of the test files:

a

1
2
3
4
5 the original line the original line the original line the original line
6
7
8
9
10
11
12
13
14
15 the original line the original line the original line the original line
16
17
18
19
20

b

1
2
3
4
5 the original line teh original line the original line the original line
6
7
8
9
10
11
12
13
14
15 the original line the original line the original line the origlnal line
16
17
18
19
20

ydiff Git integration

ydiff integrates with Git without any configuration required.

From inside a git repository, instead of git diff, you can do just:

ydiff -s

and instead of git log:

ydiff -ls

See also: https://stackoverflow.com/questions/7669963/how-can-i-get-a-side-by-side-diff-when-i-do-git-diff/14649328#14649328

Tested on Ubuntu 16.04, git 2.18.0, ydiff 1.1.

AJM
  • 133
Ciro Santilli OurBigBook.com
  • 18,092
  • 4
  • 117
  • 102
28

If you have access to GNU diff you can use its --X-group-format options to get that effect without any additional tools:

diff --old-group-format=$'\e[0;31m%<\e[0m' \
     --new-group-format=$'\e[0;31m%>\e[0m' \
     --unchanged-group-format=$'\e[0;32m%=\e[0m' \
     file1 file2

That uses ANSI colour escape codes to get red and green, with ANSI-C quoting in the shell to access the \e escapes.

--old-group-format and --new-group-format identify non-matching lines and insert them between red and colour reset codes using %< and %>, while --unchanged-group-format inserts matching lines between green and reset codes.

You can also use --old-line-format (etc), at the expense of redundant colour escapes on every line: --old-line-format=$'\e[0;31m%L\e[0m'.

Michael Homer
  • 76,565
  • when I run it gives diff: 0653-821 illegal option -- - diff: 0653-821 illegal option -- o diff: 0653-821 illegal option -- d diff: 0653-821 illegal option -- - diff: 0653-821 illegal option -- g diff: 0653-821 illegal option -- o like errors. – Aman Apr 16 '15 at 11:26
  • Hormer when i am running your commands as by one line at a time it is giving new line no output -bash-4.2$ --new-group-format=$'\e[0;31m%>\e[0m' \
    – Aman Apr 16 '15 at 11:32
  • May I configure that by default? – Eugen Konkov Jul 06 '18 at 11:22
  • 1
    @EugenKonkov You could set up an alias or function in your shell to run that for diff. – Michael Homer Jul 06 '18 at 19:35
  • This gave me a bit of trouble trying to get it to work. Unchanged stuff was showing up as added... gave up and installed colordiff – Brian Peterson Aug 02 '19 at 17:49
  • @BrianPeterson Isn't that because the suggested solution shows changed lines in red (both old and new stuff) and unchanged lines in green, while we are used to red meaning "old/removed", green meaning "new/added" and white/default color meaning "unchanged"? I've also been confused by this... – Ansa211 Jan 25 '21 at 09:48
  • When I pipe the output into less, I have to use --old-line-format rather than --old-group-format to get proper colouring of multi-line changes. – Ansa211 Jan 25 '21 at 10:13
  • 1
    @Ansa211 That is what the question asked for, rather than traditional colordiff-style output, but you can shift the green escape (32) into --new-group-format and drop the unchanged entry entirely to get that (though diff --color or colordiff is almost certainly better now if you don't have those very specific requirements - and it uses line-by-line colouring so difff --color ... | less -R doesn't break it). – Michael Homer Jan 25 '21 at 21:15
  • This worked perfectly for me! – Tyler R. Jan 04 '22 at 16:04
  • This was the solution for me on RHEL 8 – Jason R Stevens CFA May 19 '22 at 20:23
13

Try colordiff file1 file2

Availability of colordiff with your Linux/BSD distribution

Those running Debian or Ubuntu (or any of their derivatives) can probably just use "apt-get install colordiff" to download and install; colordiff is also packaged for a number of other Linux, UNIX and BSD distributions and operating systems.

(Quote from http://www.colordiff.org/)

modesto
  • 241
  • 1
    Perfect. For me, diff --color=auto was only colourising line markers and the first line of each +/- section. Piping to less -SR makes for easy browsing. – Walf Oct 18 '18 at 01:03
10

if you have vim installed, you can do diff file1 file2 | vim -

Vim will recognise diff format and give it proper coloring. The dash at the end is to let vim accept input from diff command.

TrongBang
  • 201
7

Coloured, word-level diff ouput

Here's what you can do with the the below script and diff-highlight:

Coloured diff screenshot

#!/bin/sh -eu

# Use diff-highlight to show word-level differences

diff -U3 --minimal "$@" |
  sed 's/^-/\x1b[1;31m-/;s/^+/\x1b[1;32m+/;s/^@/\x1b[1;34m@/;s/$/\x1b[0m/' |
  diff-highlight

(Credit to @retracile's answer for the sed highlighting)

αғsнιη
  • 41,407
Tom Hale
  • 30,455
  • 1
    Actually, diff-highlight does not do a word-level output. It just detects a common prefix and a common suffix on a changed line. For instance, if several words are changed, it will highlight everything from the first changed word to the last changed word. Moreover, this is effective only when only one line is changed between unchanged lines. In GNU Emacs, diff-mode does much better. – vinc17 Mar 11 '22 at 23:32
  • Installation: pip3 install --user diff-highlight – Alexey Shrub May 02 '23 at 17:29
4

Character-level color diff: Install ccdiff

ccdiff -r /usr/share/dict/words /tmp/new-dict

Output of ccdiff

apricot
  • 141
3

You could use :

  1. diff --color=auto file1 file2

  2. colordiff file1 file2

  3. git diff file1 file2 which is my favorite method

I currently use git diff or pipe it's output with colordiff :

diff() { git diff --no-index "$1" "$2" | colordiff; }

  • 1
    I like git diff --no-index too but I think files need to be seekable. (At least, doesn't work with bash process substitution for me) – Karl Aug 07 '18 at 07:06
  • @Karl Figure that part out yourself, maybe there is a Terminal setting that does it (or) some other way to make it seekable. I've been using git diff from a long time and the file is seekable for me, otherwise there'd be no use of me doing it right. – himanshuxd Aug 07 '18 at 17:26
  • 2
    Perhaps I wasn't clear very clear. As a (silly) example, this works for me diff --color <(ls | head -n+3) <(ls | tail -n +5) but not with git diff. Admittedly, not a common case or too hard to work around. – Karl Aug 08 '18 at 02:11
  • git diff --no-index already colorizes the output, why on earth do you pass the output to colordiff? If the output has no color then you have wrong config, use git config color.diff auto to fix it – phuclv Jan 25 '23 at 03:25
  • @phuclv at the time which is 2018, I think whatever terminal I was using was not coloring it with git diff itself or the color scheme might have been weird without the colordiff pipe. – himanshuxd Nov 22 '23 at 21:10
3

To change diff's color palette use the --palette option (diff version 3.4 and higher):

diff --color=always --palette='ad=1;3;38;5;154:de=1;3;38;5;9' file1 file2

Here ad and de define the color of added and deleted text respectively. For a detailed description of diff options see man diff or info diff.

2

This script uses the standard pre-version 3.4 diff (it should work with any version of diff) and colorizes the output without changing the output format in any way. It works with the latest version of RHEL (version 7.5) which has GNU diff version 3.3. Just put it in your ~/bin directory or anywhere else in your path (I suggest calling it "cdiff").

#!/bin/bash
file1color="$(tput setaf 1)"
file2color="$(tput setaf 2)"
sepcolor="$(tput setaf 6)"
reset="$(tput sgr0)"
diff $* |sed -e "s/^\\(<.*\$\\)/$file1color\\1$reset/;s/^\\(>.*\$\\)/$file2color\\1$reset/;s/^\\(---\$\\)/$sepcolor\\1$reset/"
  • 2
    And if you don't know off the top of your head what those parms to tput mean, do a man terminfo – Mark Stewart Nov 05 '20 at 15:53
  • 1
    This saved me! I am on a system with diff 3.3, so it has no --color option (nor can I upgrade, or install colordiff, or anything new). Modifying the --new-line-format and etc. made it hard to keep the lines that normal diff -u chooses. This solution was exactly what I need. – Ajean Nov 02 '22 at 21:58
1

You should have a look at the hl command available on github : git clone http://github.com/mbornet-hl/hl and on : http://www.flashnux.com/notes/page_000022_US.html

hl is a Linux command written in C, especially designed to color a text file or the output of a command. You can use up to 42 colors simultaneously, and use a configuration file to simplify command lines. You can colorize the output of every command that can be piped to another one. And if you know what regular expressions are, it will be very easy for you to use. You can use the man page to understand how to use it.
hl is very easy to use and to configure. You even can use the hl_generic script to colorize commands output without modifying their syntax.
You can, for example, colorize the output of the diff command just by typing your usual command :

diff file1 file2

If you need some help, just send me an e-mail.
Regards.

Vinz
  • 2,150
  • 14
  • 16
mbornet
  • 11
  • 1
1

Just a note: to get the "side-by-side" output you need "--color=always". You can also paginate it with less and retain the colored output:

diff -y --color=always file1 file2 | less -R

And yet another hint: try to keep the "--color=always" switch at the end. Reason? With dmesg from util-linux 2.27.1:

dmesg --human --color=always | less -R    # works
dmesg --color=always --human | less -R    # doesn't work
Gus
  • 11
1

There is a really neat tool built with python on Github at the moment called icdiff. Produces nice coloured outputs which are also “severity” aware. I use it all the time, well worth checking out.

1

Install Generic Colouriser (grc) and:

grc diff file1 file2

Available on both Linux and MacOS.

Sohail Si
  • 151
  • 4
0

If you're on an apline linux container and don't want/can't pull down these other tools, this might work in a pinch.

RED="\\\033[0;31m"
GREEN="\\\033[0;32m"
OFF="\\\033[0m"
DIFF=$(diff --label "GOT" <(echo "$OUTPUT") --label "EXPECTED" <(echo "$EXPECTED_OUTPUT"))
printf -- "$(echo "$DIFF" | sed "s|^-|$RED-|g" | sed "s|^+|$GREEN+|g" | sed "s|$|$OFF|g")"

Here's some sample output.

sample output

yurisich
  • 101
0

diff 3.4+ can colorize its output in some modes like diff -u, diff -c. Also the pure shell script is available https://github.com/ildar-shaimordanov/colordiff. It does almost the same work as the Perl https://github.com/daveewart/colordiff.

jsxt
  • 101