3

My scenario is that:

First generate side by side diff of two files using:

diff -y --supress-common-lines file1.txt file2.txt > DiffResult.txt

Output of DiffResult.txt:

   file1.txt             file2.txt
This is line A    |   This is line B
This is line C    |   This is line D

Now lets say the line

This is line A

and

This is line B

are in line 5 of file1.txt and file2.txt respectively. Then I should be able to associate the appropriate line number to it as follows:

Desired output of DiffResult.txt:

      file1.txt                file2.txt
5   This is line A    |  5   This is line B
7   This is line C    |  7   This is line D

The reason why I am going for this approach is because if I generate line numbers before diff, then even for a small white space change, the diff would show a difference because of the line numbers associated with the lines.

Anyone with bright ideas? I think this is the toughest question that has ever been asked in StackExchange, I believe :D

  • use commands like nl to add lines numbers to your input file and then pass onto diff.. and whats with snide remarks in question? – Sundeep Sep 24 '16 at 8:46
  • As mentioned in the question, if i generate line numbers before (using nl or any other command) and pass it to diff, for any space changes, the diff will be shown which i don't want to happen. – Ronit Mishra Sep 24 '16 at 14:42
  • can you show example input files where pre-processing to add line numbers will result in that behavior, by default diff will report even whitespace mismatches.. and either way, see man diff and use options to ignore white-space mismatches being reported in output – Sundeep Sep 25 '16 at 5:13
  • Even if you use ignore white spaces, the displacement caused by white spaces or newlines will show change in line numbers right so diff will fail in this case. – Ronit Mishra Sep 26 '16 at 18:24
  • again, you are only saying so and so would happen without giving an example to back up your claims... if you give example input files with all your corner cases, someone would be able to suggest some solution – Sundeep Sep 27 '16 at 2:31
1

The problem can be solved by filtering the output of diff. This example works for me (though the placement and size of the gutter between left/right sides of the diff output are likely to be a detail that differs between implementations):

#!/bin/sh
# $Id: diff-two-column,v 1.2 2016/09/26 20:38:32 tom Exp $
# see http://unix.stackexchange.com/questions/312025/how-to-associate-line-number-from-a-file-to-the-side-by-side-diff-output-result

usage() {
    cat >&2 <<EOF
usage: $0 file1 file2
EOF
    exit 1
}

[ $# = 2 ] || usage
[ -f "$1" ] || usage
[ -f "$2" ] || usage

width=${COLUMNS:-80}
check=$(stty size|cut -d' ' -f2)
[ -n "$check" ] && width=$check

diff -W $width -y "$1" "$2" | \
expand | \
awk -v width=$width '
BEGIN {
    L=0;
    R=0;
    gutter = width / 2;
    half = gutter - 2;
}
{
    textL = substr($0, 1, half - 1);
    sub("[ ]+$", "", textL);  # trim trailing blanks

    # The script relies on correctly extracting textM, the gutter:
    # if lines differ,    textM is " ! "
    # if line inserted,   textM is " > "
    # if line deleted,    textM is " < "
    # if lines unchanged, textM is "   "
    textM = substr($0, gutter - 2, 3);

    textR = ( length($0) > gutter ) ? substr($0, gutter+1, half) : "";

    if ( textM != " > " ) {
         L++;
    }
    if ( textM != " < " ) {
         R++;
    }

    if ( textL != textR ) {
        # printf "SHOW %s\n", $0;
        # printf "gap \"%s\"\n", textM;
        # printf "<<< \"%s\"\n", textL;
        # printf ">>> \"%s\"\n", textR;
        if ( textL == "" ) {
            printf "%5s %-*s %-3s %5d %s\n",
                " ", half, textL,
                textM,
                R, textR;
        } else if ( textR == "" ) {
            printf "%5d %-*s %-3s %5s %s\n",
                L, half, textL,
                textM,
                " ", textR;
        } else {
            printf "%5d %-*s %-3s %5d %s\n",
                L, half, textL,
                textM,
                R, textR;
        }
    } else {
        # printf "SKIP %s\n", $0;
    }
}
'

You cannot add line numbers before diff, because if there are insertions or deletions, the line numbers starting at that point will not match, making the differences not useful. My script computes the line numbers for the left/right sides of the difference in the awk script:

  • It first decides how wide to make the diff, based on the width of the terminal.
  • There is (in GNU diff 3.2 which I tested) a gutter (unused space) in the middle of the side-by-side differences. Starting with an 80-column terminal, I determined a way to compute the position of the gutter.
  • After initializing, the script extracts from each line (in awk, this is $0) the left (textL) and right (textR) strings, and tests if they are empty (which would happen if there were an insert/delete).
  • If the left/right lines are different, the script reconstructs the diff output, but adding the line-numbers.

Given this on the left

1
2
3
4
This is line A
6
This is line C
123456789.123456789.123456789.123456789.123456789.

yyy

and this on the right

1
2
3
4
This is line B
6
This is line D
abcdefghi.abcdefghi.abcdefghi.abcdefghi.abcdefghi.
xxx

(10 lines on the left, 9 on the right), this script produces

    5 This is line A                          |      5 This is line B
    7 This is line C                          |      7 This is line D
    8 123456789.123456789.123456789.1234567   |      8 abcdefghi.abcdefghi.abcdefghi.abcdefg
                                              |      9 xxx
   10 yyy                                     <        
  • Hi Thomas, thanks for the response. Can you explain for me what is the logic behind the snippet ? Is there any shortcut other than this ? :D – Ronit Mishra Sep 26 '16 at 18:25
  • I noticed that it didn't handle blank lines - here's an update with example. You may find it helpful to uncomment the printf's which show details. – Thomas Dickey Sep 26 '16 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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